diff --git a/front/src/index.js b/front/src/index.js
index b5d7dc9..e827e0a 100644
--- a/front/src/index.js
+++ b/front/src/index.js
@@ -7,6 +7,7 @@ import { Root } from "./root";
import { Demo } from "./routes/demo";
import { Home } from "./routes/home";
import { Keys } from "./routes/keys";
+import { Password } from "./routes/password";
import { AuthSuccessful } from "./routes/auth_successful";
import "./index.css";
@@ -23,17 +24,13 @@ const router = createBrowserRouter([
element: ,
},
{
- path: "user",
- element: ,
+ path: "password",
+ element: ,
},
{
path: "keys",
element: ,
},
- {
- path: "faq",
- element: ,
- },
{
path: "auth-successful",
element: ,
diff --git a/front/src/root.jsx b/front/src/root.jsx
index 5ba629a..61f8615 100644
--- a/front/src/root.jsx
+++ b/front/src/root.jsx
@@ -17,13 +17,10 @@ export const Root = () => {
-
- FAQ
-
{signedIn ? (
<>
-
- User
+
+ Password
Keys
diff --git a/front/src/routes/auth_successful.jsx b/front/src/routes/auth_successful.jsx
index cb51573..7c66587 100644
--- a/front/src/routes/auth_successful.jsx
+++ b/front/src/routes/auth_successful.jsx
@@ -23,11 +23,17 @@ export const AuthSuccessful = () => {
if (signedIn) {
return (
<>
-
Authentication Successful
+
Hello there, {player?.username || ""}!
+
+ If you have not already done so:
+
+ Add a Public Key
+
+
+
- Hello there, {player?.username || ""}!
- Go Home{" "}
+ Go Home
>
diff --git a/front/src/routes/home.jsx b/front/src/routes/home.jsx
index 42734ad..4cfac77 100644
--- a/front/src/routes/home.jsx
+++ b/front/src/routes/home.jsx
@@ -14,27 +14,30 @@ export const Home = () => {
PubkeyAuthentication yes`;
return (
<>
-
Hello there, {player?.username}!
-
- You can now start playing CheSSH by using any of your imported{" "}
- public keys, or by{" "}
- creating a password.
-
-
+
Welcome, {player?.username}
-
Getting Started
+
Getting Started
- -
- Add the following to your ssh config (normally in ~/.ssh/config):
-
+
+
-
+ Add a public key, or{" "}
+ set a password.
+
+
+
+
-
+ Insert the following block in your{" "}
+ ssh config:
+
-
+
+
- Then, connect with:
diff --git a/front/src/routes/keys.jsx b/front/src/routes/keys.jsx
index 3c552a1..4dee1ce 100644
--- a/front/src/routes/keys.jsx
+++ b/front/src/routes/keys.jsx
@@ -18,12 +18,18 @@ const KeyCard = ({ onDelete, props }) => {
const { id, name, key } = props;
const deleteThisKey = () => {
- fetch(`/api/keys/${id}`, {
- credentials: "same-origin",
- method: "DELETE",
- })
- .then((r) => r.json())
- .then((d) => d.success && onDelete && onDelete());
+ if (
+ window.confirm(
+ "Are you sure? This will close all your current ssh sessions."
+ )
+ ) {
+ fetch(`/api/keys/${id}`, {
+ credentials: "same-origin",
+ method: "DELETE",
+ })
+ .then((r) => r.json())
+ .then((d) => d.success && onDelete && onDelete());
+ }
};
return (
@@ -44,13 +50,13 @@ const KeyCard = ({ onDelete, props }) => {
const AddKeyButton = ({ onSave }) => {
const [open, setOpen] = useState(false);
- const [name, setName] = useState({ value: "", error: "" });
- const [key, setKey] = useState({ value: "", error: "" });
+ const [name, setName] = useState("");
+ const [key, setKey] = useState("");
const [errors, setErrors] = useState(null);
const setDefaults = () => {
- setName({ value: "", error: "" });
- setKey({ value: "", error: "" });
+ setName("");
+ setKey("");
setErrors(null);
};
@@ -67,8 +73,8 @@ const AddKeyButton = ({ onSave }) => {
"Content-Type": "application/json",
},
body: JSON.stringify({
- key: key.value.trim(),
- name: name.value,
+ key: key.trim(),
+ name: name.trim(),
}),
})
.then((r) => r.json())
@@ -119,8 +125,8 @@ const AddKeyButton = ({ onSave }) => {
Key Name *
setName({ ...name, value: e.target.value })}
+ value={name}
+ onChange={(e) => setName(e.target.value)}
required
/>
@@ -129,8 +135,8 @@ const AddKeyButton = ({ onSave }) => {
diff --git a/front/src/routes/password.jsx b/front/src/routes/password.jsx
new file mode 100644
index 0000000..11fb775
--- /dev/null
+++ b/front/src/routes/password.jsx
@@ -0,0 +1,144 @@
+import { useState } from "react";
+import { Link } from "react-router-dom";
+
+export const Password = () => {
+ const [password, setPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+ const [errors, setErrors] = useState(null);
+ const [success, setSuccess] = useState(false);
+
+ const resetFields = () => {
+ setErrors(null);
+ setPassword("");
+ setConfirmPassword("");
+ };
+
+ const reset = () => {
+ resetFields();
+ setSuccess(false);
+ };
+
+ const deletePassword = () => {
+ if (
+ window.confirm(
+ "Are you sure? This will close all your current ssh sessions."
+ )
+ ) {
+ fetch(`/api/player/token/password`, {
+ method: "DELETE",
+ credentials: "same-origin",
+ })
+ .then((r) => r.json())
+ .then((r) => {
+ if (r.success) {
+ resetFields();
+ setSuccess(true);
+ }
+ });
+ }
+ };
+
+ const submitPassword = () => {
+ if (
+ window.confirm(
+ "Are you sure? This will close all your current ssh sessions."
+ )
+ ) {
+ fetch(`/api/player/token/password`, {
+ method: "PUT",
+ credentials: "same-origin",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ password,
+ password_confirmation: confirmPassword,
+ }),
+ })
+ .then((r) => r.json())
+ .then((p) => {
+ if (p.success) {
+ resetFields();
+ setSuccess(true);
+ } else if (p.errors) {
+ if (typeof p.errors === "object") {
+ setErrors(
+ Object.keys(p.errors).map(
+ (field) => `${field}: ${p.errors[field].join(",")}`
+ )
+ );
+ } else {
+ setErrors([p.errors]);
+ }
+ }
+ });
+ }
+ };
+
+ return (
+ <>
+
+
Update SSH Password
+
+ An SSH password allows you to connect from any device. However, it is
+ inherently less secure than a public key.
+
+
Use a password at your own risk.
+
+
+
+
Previously set a password and no longer want it?
+
+
+
+
Or if you're dead set on it...
+
+
Password *
+
setPassword(e.target.value)}
+ type="password"
+ required
+ />
+
+
+
Confirm Password *
+
setConfirmPassword(e.target.value)}
+ required
+ />
+
+
+ {errors && (
+
+ {errors.map((error, i) => (
+
{error}
+ ))}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ {success &&
Password updated
}
+
+ >
+ );
+};
diff --git a/lib/chessh/release.ex b/lib/chessh/release.ex
index bae0c08..82f1807 100644
--- a/lib/chessh/release.ex
+++ b/lib/chessh/release.ex
@@ -16,4 +16,3 @@ defmodule Chessh.Release do
Application.fetch_env!(@app, :ecto_repos)
end
end
-
diff --git a/lib/chessh/web/web.ex b/lib/chessh/web/web.ex
index 9539c9a..8c0929a 100644
--- a/lib/chessh/web/web.ex
+++ b/lib/chessh/web/web.ex
@@ -43,7 +43,25 @@ defmodule Chessh.Web.Endpoint do
|> assign_jwt_and_redirect_or_encode(status, body)
end
- put "/player/password" do
+ delete "/player/token/password" do
+ player = get_player_from_jwt(conn)
+ PlayerSession.close_all_player_sessions(player)
+
+ {status, body} =
+ case Repo.update(Ecto.Changeset.change(player, %{hashed_password: nil})) do
+ {:ok, _new_player} ->
+ {200, %{success: true}}
+
+ {:error, _} ->
+ {400, %{success: false}}
+ end
+
+ conn
+ |> put_resp_content_type("application/json")
+ |> send_resp(status, Jason.encode!(body))
+ end
+
+ put "/player/token/password" do
player = get_player_from_jwt(conn)
PlayerSession.close_all_player_sessions(player)
@@ -68,44 +86,6 @@ defmodule Chessh.Web.Endpoint do
|> send_resp(status, Jason.encode!(body))
end
- post "/player/login" do
- {status, body} =
- case conn.body_params do
- %{"username" => username, "password" => password} ->
- player = Repo.get_by(Player, username: username)
-
- case Player.valid_password?(player, password) do
- true ->
- {
- 200,
- %{
- token:
- Token.generate_and_sign!(%{
- "uid" => player.id
- })
- }
- }
-
- _ ->
- {
- 400,
- %{
- errors: "Invalid credentials"
- }
- }
- end
-
- _ ->
- {
- 400,
- %{errors: "Username and password must be defined"}
- }
- end
-
- conn
- |> assign_jwt_and_redirect_or_encode(status, body)
- end
-
get "/player/logout" do
conn
|> delete_resp_cookie("jwt")