WIP: Migrate to matrix-rust-sdk #111
@@ -2,7 +2,7 @@ use crate::app_config::AppConfig;
|
|||||||
use crate::controllers::{HttpFailure, HttpResult};
|
use crate::controllers::{HttpFailure, HttpResult};
|
||||||
use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod};
|
use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod};
|
||||||
use crate::extractors::session_extractor::MatrixGWSession;
|
use crate::extractors::session_extractor::MatrixGWSession;
|
||||||
use crate::users::{User, UserEmail};
|
use crate::users::{ExtendedUserInfo, User, UserEmail};
|
||||||
use actix_remote_ip::RemoteIP;
|
use actix_remote_ip::RemoteIP;
|
||||||
use actix_web::{HttpResponse, web};
|
use actix_web::{HttpResponse, web};
|
||||||
use light_openid::primitives::OpenIDConfig;
|
use light_openid::primitives::OpenIDConfig;
|
||||||
@@ -108,7 +108,7 @@ pub async fn finish_oidc(
|
|||||||
|
|
||||||
/// Get current user information
|
/// Get current user information
|
||||||
pub async fn auth_info(auth: AuthExtractor) -> HttpResult {
|
pub async fn auth_info(auth: AuthExtractor) -> HttpResult {
|
||||||
Ok(HttpResponse::Ok().json(auth.user))
|
Ok(HttpResponse::Ok().json(ExtendedUserInfo::from_user(auth.user).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign out user
|
/// Sign out user
|
||||||
|
|||||||
@@ -166,3 +166,19 @@ impl APIToken {
|
|||||||
(self.last_used + self.max_inactivity) < time_secs()
|
(self.last_used + self.max_inactivity) < time_secs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, Debug, Clone)]
|
||||||
|
pub struct ExtendedUserInfo {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub user: User,
|
||||||
|
pub matrix_user_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtendedUserInfo {
|
||||||
|
pub async fn from_user(user: User) -> anyhow::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
user,
|
||||||
|
matrix_user_id: None, // TODO
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { ServerApi } from "./api/ServerApi";
|
|||||||
import { LoginRoute } from "./routes/auth/LoginRoute";
|
import { LoginRoute } from "./routes/auth/LoginRoute";
|
||||||
import { OIDCCbRoute } from "./routes/auth/OIDCCbRoute";
|
import { OIDCCbRoute } from "./routes/auth/OIDCCbRoute";
|
||||||
import { HomeRoute } from "./routes/HomeRoute";
|
import { HomeRoute } from "./routes/HomeRoute";
|
||||||
|
import { MatrixLinkRoute } from "./routes/MatrixLinkRoute";
|
||||||
import { NotFoundRoute } from "./routes/NotFoundRoute";
|
import { NotFoundRoute } from "./routes/NotFoundRoute";
|
||||||
import { BaseLoginPage } from "./widgets/auth/BaseLoginPage";
|
import { BaseLoginPage } from "./widgets/auth/BaseLoginPage";
|
||||||
import BaseAuthenticatedPage from "./widgets/dashboard/BaseAuthenticatedPage";
|
import BaseAuthenticatedPage from "./widgets/dashboard/BaseAuthenticatedPage";
|
||||||
@@ -37,6 +38,7 @@ export function App(): React.ReactElement {
|
|||||||
signedIn || ServerApi.Config.auth_disabled ? (
|
signedIn || ServerApi.Config.auth_disabled ? (
|
||||||
<Route path="*" element={<BaseAuthenticatedPage />}>
|
<Route path="*" element={<BaseAuthenticatedPage />}>
|
||||||
<Route path="" element={<HomeRoute />} />
|
<Route path="" element={<HomeRoute />} />
|
||||||
|
<Route path="matrix_link" element={<MatrixLinkRoute />} />
|
||||||
<Route path="*" element={<NotFoundRoute />} />
|
<Route path="*" element={<NotFoundRoute />} />
|
||||||
</Route>
|
</Route>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface UserInfo {
|
|||||||
time_update: number;
|
time_update: number;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
matrix_user_id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TokenStateKey = "auth-state";
|
const TokenStateKey = "auth-state";
|
||||||
|
|||||||
15
matrixgw_frontend/src/api/MatrixLinkApi.ts
Normal file
15
matrixgw_frontend/src/api/MatrixLinkApi.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { APIClient } from "./ApiClient";
|
||||||
|
|
||||||
|
export class MatrixLinkApi {
|
||||||
|
/**
|
||||||
|
* Start Matrix Account login
|
||||||
|
*/
|
||||||
|
static async StartAuth(): Promise<{ url: string }> {
|
||||||
|
return (
|
||||||
|
await APIClient.exec({
|
||||||
|
uri: "/matrix_link/start_auth",
|
||||||
|
method: "POST",
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
import { useUserInfo } from "../widgets/dashboard/BaseAuthenticatedPage";
|
||||||
|
import { NotLinkedAccountMessage } from "../widgets/NotLinkedAccountMessage";
|
||||||
|
|
||||||
export function HomeRoute(): React.ReactElement {
|
export function HomeRoute(): React.ReactElement {
|
||||||
|
const user = useUserInfo();
|
||||||
|
|
||||||
|
if (!user.info.matrix_user_id) return <NotLinkedAccountMessage />;
|
||||||
|
|
||||||
return <p>Todo home route</p>;
|
return <p>Todo home route</p>;
|
||||||
}
|
}
|
||||||
|
|||||||
98
matrixgw_frontend/src/routes/MatrixLinkRoute.tsx
Normal file
98
matrixgw_frontend/src/routes/MatrixLinkRoute.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import LinkIcon from "@mui/icons-material/Link";
|
||||||
|
import LinkOffIcon from "@mui/icons-material/LinkOff";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActions,
|
||||||
|
CardContent,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { MatrixLinkApi } from "../api/MatrixLinkApi";
|
||||||
|
import { useAlert } from "../hooks/contexts_provider/AlertDialogProvider";
|
||||||
|
import { useLoadingMessage } from "../hooks/contexts_provider/LoadingMessageProvider";
|
||||||
|
import { useUserInfo } from "../widgets/dashboard/BaseAuthenticatedPage";
|
||||||
|
import { MatrixGWRouteContainer } from "../widgets/MatrixGWRouteContainer";
|
||||||
|
|
||||||
|
export function MatrixLinkRoute(): React.ReactElement {
|
||||||
|
const user = useUserInfo();
|
||||||
|
return (
|
||||||
|
<MatrixGWRouteContainer label={"Matrix account link"}>
|
||||||
|
{user.info.matrix_user_id === null ? <ConnectCard /> : <ConnectedCard />}
|
||||||
|
</MatrixGWRouteContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConnectCard(): React.ReactElement {
|
||||||
|
const alert = useAlert();
|
||||||
|
const loadingMessage = useLoadingMessage();
|
||||||
|
|
||||||
|
const startMatrixConnection = async () => {
|
||||||
|
try {
|
||||||
|
loadingMessage.show("Initiating Matrix link...");
|
||||||
|
|
||||||
|
const res = await MatrixLinkApi.StartAuth();
|
||||||
|
|
||||||
|
window.location.href = res.url;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Failed to connect to Matrix account! ${e}`);
|
||||||
|
alert(`Failed to connect to Matrix account! ${e}`);
|
||||||
|
} finally {
|
||||||
|
loadingMessage.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h5" component="div" gutterBottom>
|
||||||
|
<i>Disconnected from your Matrix account</i>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="body1" gutterBottom>
|
||||||
|
You need to connect MatrixGW to your Matrix account to let it access
|
||||||
|
your messages.
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
<CardActions>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<LinkIcon />}
|
||||||
|
onClick={startMatrixConnection}
|
||||||
|
>
|
||||||
|
Connect now
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConnectedCard(): React.ReactElement {
|
||||||
|
const user = useUserInfo();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h5" component="div" gutterBottom>
|
||||||
|
<i>Connected to your Matrix account</i>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="body1" gutterBottom>
|
||||||
|
<p>
|
||||||
|
MatrixGW is currently connected to your account with ID{" "}
|
||||||
|
<i>{user.info.matrix_user_id}</i>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you encounter issues with your Matrix account you can try to
|
||||||
|
disconnect and connect back again.
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
<CardActions>
|
||||||
|
<Button size="small" variant="outlined" startIcon={<LinkOffIcon />}>
|
||||||
|
Disconnect
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
37
matrixgw_frontend/src/widgets/MatrixGWRouteContainer.tsx
Normal file
37
matrixgw_frontend/src/widgets/MatrixGWRouteContainer.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Typography } from "@mui/material";
|
||||||
|
import React, { type PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
export function MatrixGWRouteContainer(
|
||||||
|
p: {
|
||||||
|
label: string | React.ReactElement;
|
||||||
|
actions?: React.ReactElement;
|
||||||
|
} & PropsWithChildren
|
||||||
|
): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
margin: "50px",
|
||||||
|
flexGrow: 1,
|
||||||
|
flexShrink: 0,
|
||||||
|
flexBasis: 0,
|
||||||
|
minWidth: 0,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h4">{p.label}</Typography>
|
||||||
|
{p.actions ?? <></>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{p.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
14
matrixgw_frontend/src/widgets/NotLinkedAccountMessage.tsx
Normal file
14
matrixgw_frontend/src/widgets/NotLinkedAccountMessage.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export function NotLinkedAccountMessage(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Your Matrix account is not linked yet!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -124,7 +124,6 @@ export default function BaseAuthenticatedPage(): React.ReactElement {
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
padding: "50px",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
@@ -137,6 +136,6 @@ export default function BaseAuthenticatedPage(): React.ReactElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function userUserInfo(): UserInfoContext {
|
export function useUserInfo(): UserInfoContext {
|
||||||
return React.use(UserInfoContextK)!;
|
return React.use(UserInfoContextK)!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import Tooltip from "@mui/material/Tooltip";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { RouterLink } from "../RouterLink";
|
import { RouterLink } from "../RouterLink";
|
||||||
import { userUserInfo as useUserInfo } from "./BaseAuthenticatedPage";
|
import { useUserInfo } from "./BaseAuthenticatedPage";
|
||||||
import ThemeSwitcher from "./ThemeSwitcher";
|
import ThemeSwitcher from "./ThemeSwitcher";
|
||||||
|
|
||||||
const AppBar = styled(MuiAppBar)(({ theme }) => ({
|
const AppBar = styled(MuiAppBar)(({ theme }) => ({
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default function DashboardSidebar({
|
|||||||
<DashboardSidebarPageItem
|
<DashboardSidebarPageItem
|
||||||
title="Matrix link"
|
title="Matrix link"
|
||||||
icon={<Icon path={mdiLinkLock} size={"1.5em"} />}
|
icon={<Icon path={mdiLinkLock} size={"1.5em"} />}
|
||||||
href="/matrixlink"
|
href="/matrix_link"
|
||||||
/>
|
/>
|
||||||
<DashboardSidebarPageItem
|
<DashboardSidebarPageItem
|
||||||
title="API tokens"
|
title="API tokens"
|
||||||
|
|||||||
Reference in New Issue
Block a user