Skip to content

Commit

Permalink
more authentication fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
garethgeorge committed Feb 1, 2024
1 parent 8739b7d commit 3ed7ade
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 44 deletions.
2 changes: 1 addition & 1 deletion internal/api/authenticationhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (s *AuthenticationHandler) Login(ctx context.Context, req *connect.Request[
user, err := s.authenticator.Login(req.Msg.Username, req.Msg.Password)
if err != nil {
zap.L().Warn("failed login attempt", zap.Error(err))
return nil, auth.ErrInvalidPassword
return nil, connect.NewError(connect.CodeUnauthenticated, auth.ErrInvalidPassword)
}

token, err := s.authenticator.CreateJWT(user)
Expand Down
2 changes: 1 addition & 1 deletion internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func NewAuthenticator(key []byte, configProvider config.ConfigStore) *Authentica
}

var ErrUserNotFound = errors.New("user not found")
var ErrInvalidPassword = errors.New("wrong password")
var ErrInvalidPassword = errors.New("invalid password")

func (a *Authenticator) users() []*v1.User {
config, err := a.config.Get()
Expand Down
2 changes: 1 addition & 1 deletion proto/v1/operations.proto
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ message OperationStats {
message OperationRunHook {
string name = 1; // description of the hook that was run. typically repo/hook_idx or plan/hook_idx.
string output_ref = 2; // reference to the output of the hook.
}
}
67 changes: 36 additions & 31 deletions webui/src/components/OperationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
SaveOutlined,
DeleteOutlined,
DownloadOutlined,
RobotOutlined,
} from "@ant-design/icons";
import { BackupProgressEntry, ResticSnapshot } from "../../gen/ts/v1/restic_pb";
import {
Expand Down Expand Up @@ -110,9 +111,6 @@ export const OperationList = ({
}, [JSON.stringify(req)]);
} else {
backups = [...(useBackups || [])];
backups.sort((a, b) => {
return b.startTimeMs - a.startTimeMs;
});
}

if (backups.length === 0) {
Expand All @@ -124,28 +122,22 @@ export const OperationList = ({
);
}

const operations = backups.flatMap((b) => b.operations);
operations.sort((a, b) => {
return Number(b.unixTimeStartMs - a.unixTimeStartMs)
});

return (
<List
itemLayout="horizontal"
size="small"
dataSource={backups}
renderItem={(backup) => {
const ops = [...backup.operations];
ops.reverse();
return (
<Card size="small" style={{ margin: "5px" }}>
{ops.map((op) => {
if (shouldHideOperation(op)) {
return null;
}
return <OperationRow alertApi={alertApi!} key={op.id} operation={op} showPlan={showPlan || false} />
})}
</Card>
);
dataSource={operations}
renderItem={(op) => {
return <OperationRow alertApi={alertApi!} key={op.id} operation={op} showPlan={showPlan || false} />
}}
pagination={
backups.length > 10
? { position: "both", align: "center", defaultPageSize: 10 }
operations.length > 25
? { position: "both", align: "center", defaultPageSize: 25 }
: undefined
}
/>
Expand Down Expand Up @@ -186,6 +178,9 @@ export const OperationRow = ({
case DisplayType.PRUNE:
avatar = <DeleteOutlined style={{ color: details.color }} />;
break;
case DisplayType.RUNHOOK:
avatar = <RobotOutlined style={{ color: details.color }} />;

}

const opName = displayTypeToString(getTypeForDisplay(operation));
Expand Down Expand Up @@ -483,33 +478,43 @@ const ForgetOperationDetails = ({ forgetOp }: { forgetOp: OperationForget }) =>
}

const RunHookOperationStatus = ({ op }: { op: Operation }) => {
const [output, setOutput] = useState<string | undefined>(undefined);

if (op.op.case !== "operationRunHook") {
return <>Wrong operation type</>;
}

const hook = op.op.value;

return <>
<Collapse size="small" destroyInactivePanel items={[
{
key: 1,
label: "Logs for hook " + hook.name,
children: <>
<BigOperationDataVerbatim id={op.id!} outputRef={hook.outputRef} />
</>
},
]} />
</>
}

// TODO: refactor this to use the provider pattern
const BigOperationDataVerbatim = ({ id, outputRef }: { id: bigint, outputRef: string }) => {
const [output, setOutput] = useState<string | undefined>(undefined);

useEffect(() => {
if (!hook.outputRef) {
if (!outputRef) {
return;
}
backrestService.getBigOperationData(new OperationDataRequest({
id: op.id,
key: hook.outputRef,
id: id,
key: outputRef,
})).then((resp) => {
setOutput(new TextDecoder("utf-8").decode(resp.value));
}).catch((e) => {
console.error("Failed to fetch hook output: ", e);
});
}, [hook.outputRef]);
}, [id, outputRef]);

return <>
Hook: {hook.name} <br />
Output: <br />
<pre>
{output}
</pre>
</>
return <pre>{output}</pre>;
}
12 changes: 11 additions & 1 deletion webui/src/views/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import _ from "lodash";
import { Code } from "@connectrpc/connect";
import { LoginModal } from "./LoginModal";
import { SettingsModal } from "./SettingsModal";
import { backrestService } from "../api";
import { backrestService, setAuthToken } from "../api";
import { MainContentArea, useSetContent } from "./MainContentArea";
import { GettingStartedGuide } from "./GettingStartedGuide";
import { useConfig } from "../components/ConfigProvider";
Expand Down Expand Up @@ -120,6 +120,16 @@ export const App: React.FC = () => {
{config && config.host ? "Host: " + config.host : undefined}
</small>
</h1>
<Button
type="text"
style={{ position: "absolute", right: "10px" }}
onClick={() => {
setAuthToken("");
window.location.reload();
}}
>
Logout
</Button>
</Header>
<Layout>
<Sider width={300} style={{ background: colorBgContainer }}>
Expand Down
11 changes: 2 additions & 9 deletions webui/src/views/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@ import { authenticationService, setAuthToken } from '../api';
import { LoginRequest } from '../../gen/ts/v1/authentication_pb';
import { useAlertApi } from '../components/Alerts';

const credentialsKey = "backrest-ui-credentials";

export const LoginModal = () => {
let defaultCreds = new LoginRequest();
const credsInStorage = localStorage.getItem(credentialsKey);
if (credsInStorage) {
defaultCreds = LoginRequest.fromJson(JSON.parse(credsInStorage));
}

const [form] = Form.useForm();
const alertApi = useAlertApi()!;
Expand All @@ -36,16 +30,15 @@ export const LoginModal = () => {
password: values.password,
});

localStorage.setItem(credentialsKey, loginReq.toJsonString());
try {
const loginResponse = await authenticationService.login(loginReq);
setAuthToken(loginResponse.token);
alertApi.success("Logged in", 5);
setTimeout(() => {
window.location.reload();
}, 500);
} catch (e) {
alertApi.error("Login failed: " + e, 10);
} catch (e: any) {
alertApi.error("Login failed: " + (e.message ? e.message : '' + e), 10);
}
};

Expand Down

0 comments on commit 3ed7ade

Please sign in to comment.