authenticate: show the device enrolled page as the user info page (#3151)

This commit is contained in:
Caleb Doxsey 2022-03-17 12:15:57 -06:00 committed by GitHub
parent bb7de0d227
commit da97546de1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 109 additions and 76 deletions

View file

@ -100,7 +100,12 @@ func (a *Authenticate) mountDashboard(r *mux.Router) {
sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut)) sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut))
sr.Path("/webauthn").Handler(a.webauthn) sr.Path("/webauthn").Handler(a.webauthn)
sr.Path("/device-enrolled").Handler(httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { sr.Path("/device-enrolled").Handler(httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
handlers.DeviceEnrolled().ServeHTTP(w, r) userInfoData, err := a.getUserInfoData(r)
if err != nil {
return err
}
handlers.DeviceEnrolled(userInfoData).ServeHTTP(w, r)
return nil return nil
})) }))
@ -505,6 +510,7 @@ func (a *Authenticate) getSessionFromCtx(ctx context.Context) (*sessions.State,
func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error { func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
ctx, span := trace.StartSpan(r.Context(), "authenticate.userInfo") ctx, span := trace.StartSpan(r.Context(), "authenticate.userInfo")
defer span.End() defer span.End()
r = r.WithContext(ctx)
// if we came in with a redirect URI, save it to a cookie so it doesn't expire with the HMAC // if we came in with a redirect URI, save it to a cookie so it doesn't expire with the HMAC
if redirectURI := r.FormValue(urlutil.QueryRedirectURI); redirectURI != "" { if redirectURI := r.FormValue(urlutil.QueryRedirectURI); redirectURI != "" {
@ -519,32 +525,42 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
return nil return nil
} }
state := a.state.Load() userInfoData, err := a.getUserInfoData(r)
authenticateURL, err := a.options.Load().GetAuthenticateURL()
if err != nil { if err != nil {
return err return err
} }
s, err := a.getSessionFromCtx(ctx) handlers.UserInfo(userInfoData).ServeHTTP(w, r)
return nil
}
func (a *Authenticate) getUserInfoData(r *http.Request) (handlers.UserInfoData, error) {
state := a.state.Load()
authenticateURL, err := a.options.Load().GetAuthenticateURL()
if err != nil {
return handlers.UserInfoData{}, err
}
s, err := a.getSessionFromCtx(r.Context())
if err != nil { if err != nil {
s.ID = uuid.New().String() s.ID = uuid.New().String()
} }
pbSession, isImpersonated, err := a.getCurrentSession(ctx) pbSession, isImpersonated, err := a.getCurrentSession(r.Context())
if err != nil { if err != nil {
pbSession = &session.Session{ pbSession = &session.Session{
Id: s.ID, Id: s.ID,
} }
} }
pbUser, err := a.getUser(ctx, pbSession.GetUserId()) pbUser, err := a.getUser(r.Context(), pbSession.GetUserId())
if err != nil { if err != nil {
pbUser = &user.User{ pbUser = &user.User{
Id: pbSession.GetUserId(), Id: pbSession.GetUserId(),
} }
} }
pbDirectoryUser, err := a.getDirectoryUser(ctx, pbSession.GetUserId()) pbDirectoryUser, err := a.getDirectoryUser(r.Context(), pbSession.GetUserId())
if err != nil { if err != nil {
pbDirectoryUser = &directory.User{ pbDirectoryUser = &directory.User{
Id: pbSession.GetUserId(), Id: pbSession.GetUserId(),
@ -552,7 +568,7 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
} }
var groups []*directory.Group var groups []*directory.Group
for _, groupID := range pbDirectoryUser.GetGroupIds() { for _, groupID := range pbDirectoryUser.GetGroupIds() {
pbDirectoryGroup, err := directory.GetGroup(ctx, state.dataBrokerClient, groupID) pbDirectoryGroup, err := directory.GetGroup(r.Context(), state.dataBrokerClient, groupID)
if err != nil { if err != nil {
pbDirectoryGroup = &directory.Group{ pbDirectoryGroup = &directory.Group{
Id: groupID, Id: groupID,
@ -563,9 +579,9 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
groups = append(groups, pbDirectoryGroup) groups = append(groups, pbDirectoryGroup)
} }
creationOptions, requestOptions, _ := a.webauthn.GetOptions(ctx) creationOptions, requestOptions, _ := a.webauthn.GetOptions(r.Context())
handlers.UserInfo(handlers.UserInfoData{ return handlers.UserInfoData{
CSRFToken: csrf.Token(r), CSRFToken: csrf.Token(r),
DirectoryGroups: groups, DirectoryGroups: groups,
DirectoryUser: pbDirectoryUser, DirectoryUser: pbDirectoryUser,
@ -577,8 +593,7 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
WebAuthnRequestOptions: requestOptions, WebAuthnRequestOptions: requestOptions,
WebAuthnURL: urlutil.WebAuthnURL(r, authenticateURL, state.sharedKey, r.URL.Query()), WebAuthnURL: urlutil.WebAuthnURL(r, authenticateURL, state.sharedKey, r.URL.Query()),
PomeriumVersion: version.FullVersion(), PomeriumVersion: version.FullVersion(),
}).ServeHTTP(w, r) }, nil
return nil
} }
func (a *Authenticate) saveSessionToDataBroker( func (a *Authenticate) saveSessionToDataBroker(

View file

@ -8,8 +8,8 @@ import (
) )
// DeviceEnrolled displays an HTML page informing the user that they've successfully enrolled a device. // DeviceEnrolled displays an HTML page informing the user that they've successfully enrolled a device.
func DeviceEnrolled() http.Handler { func DeviceEnrolled(data UserInfoData) http.Handler {
return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return ui.ServePage(w, r, "DeviceEnrolled", map[string]interface{}{}) return ui.ServePage(w, r, "DeviceEnrolled", data.ToJSON())
}) })
} }

View file

@ -1,4 +1,9 @@
import DeviceEnrolledPage from "./components/DeviceEnrolledPage"; import Box from "@mui/material/Box";
import CssBaseline from "@mui/material/CssBaseline";
import { ThemeProvider } from "@mui/material/styles";
import { get } from "lodash";
import React, { FC } from "react";
import ErrorPage from "./components/ErrorPage"; import ErrorPage from "./components/ErrorPage";
import Footer from "./components/Footer"; import Footer from "./components/Footer";
import Header from "./components/Header"; import Header from "./components/Header";
@ -8,12 +13,7 @@ import UserInfoPage from "./components/UserInfoPage";
import WebAuthnRegistrationPage from "./components/WebAuthnRegistrationPage"; import WebAuthnRegistrationPage from "./components/WebAuthnRegistrationPage";
import { SubpageContextProvider } from "./context/Subpage"; import { SubpageContextProvider } from "./context/Subpage";
import { createTheme } from "./theme"; import { createTheme } from "./theme";
import {PageData, UserInfoPageData} from "./types"; import { PageData, UserInfoPageData } from "./types";
import Box from "@mui/material/Box";
import CssBaseline from "@mui/material/CssBaseline";
import { ThemeProvider } from "@mui/material/styles";
import React, { FC } from "react";
import {get} from "lodash";
const theme = createTheme(); const theme = createTheme();
@ -21,15 +21,13 @@ const App: FC = () => {
const data = (window["POMERIUM_DATA"] || {}) as PageData; const data = (window["POMERIUM_DATA"] || {}) as PageData;
let body: React.ReactNode = <></>; let body: React.ReactNode = <></>;
switch (data?.page) { switch (data?.page) {
case "DeviceEnrolled":
body = <DeviceEnrolledPage data={data} />;
break;
case "Error": case "Error":
body = <ErrorPage data={data} />; body = <ErrorPage data={data} />;
break; break;
case "SignOutConfirm": case "SignOutConfirm":
body = <SignOutConfirmPage data={data} />; body = <SignOutConfirmPage data={data} />;
break; break;
case "DeviceEnrolled":
case "UserInfo": case "UserInfo":
body = <UserInfoPage data={data} />; body = <UserInfoPage data={data} />;
break; break;
@ -40,7 +38,7 @@ const App: FC = () => {
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<CssBaseline /> <CssBaseline />
<SubpageContextProvider> <SubpageContextProvider page={data?.page}>
<Header includeSidebar={data?.page === "UserInfo"} data={data} /> <Header includeSidebar={data?.page === "UserInfo"} data={data} />
<ToolbarOffset /> <ToolbarOffset />
<Box sx={{ overflow: "hidden", height: "calc(100vh - 120px)" }}> <Box sx={{ overflow: "hidden", height: "calc(100vh - 120px)" }}>
@ -55,7 +53,7 @@ const App: FC = () => {
<ToolbarOffset /> <ToolbarOffset />
</Box> </Box>
</Box> </Box>
<Footer pomeriumVersion={get(data, 'pomeriumVersion')} /> <Footer pomeriumVersion={get(data, "pomeriumVersion")} />
</SubpageContextProvider> </SubpageContextProvider>
</ThemeProvider> </ThemeProvider>
); );

View file

@ -1,19 +0,0 @@
import HeroSection from "./HeroSection";
import Container from "@mui/material/Container";
import React, { FC } from "react";
import { DeviceEnrolledPageData } from "src/types";
type DeviceEnrolledPageProps = {
data: DeviceEnrolledPageData;
};
const DeviceEnrolledPage: FC<DeviceEnrolledPageProps> = () => {
return (
<Container maxWidth={false}>
<HeroSection
title="Device Enrolled"
text="Device Successfully Enrolled"
/>
</Container>
);
};
export default DeviceEnrolledPage;

View file

@ -1,9 +1,18 @@
import { Drawer, useMediaQuery } from "@mui/material"; import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Drawer,
useMediaQuery,
} from "@mui/material";
import Container from "@mui/material/Container"; import Container from "@mui/material/Container";
import Stack from "@mui/material/Stack"; import Stack from "@mui/material/Stack";
import { useTheme } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles";
import React, { FC, useContext } from "react"; import React, { FC, useContext, useEffect, useState } from "react";
import { UserInfoPageData } from "src/types"; import { UserInfoData } from "src/types";
import { SubpageContext } from "../context/Subpage"; import { SubpageContext } from "../context/Subpage";
import GroupDetails from "./GroupDetails"; import GroupDetails from "./GroupDetails";
@ -13,7 +22,7 @@ import { ToolbarOffset } from "./ToolbarOffset";
import { UserSidebarContent } from "./UserSidebarContent"; import { UserSidebarContent } from "./UserSidebarContent";
type UserInfoPageProps = { type UserInfoPageProps = {
data: UserInfoPageData; data: UserInfoData & { page: "DeviceEnrolled" | "UserInfo" };
}; };
const UserInfoPage: FC<UserInfoPageProps> = ({ data }) => { const UserInfoPage: FC<UserInfoPageProps> = ({ data }) => {
const theme = useTheme(); const theme = useTheme();
@ -23,8 +32,31 @@ const UserInfoPage: FC<UserInfoPageProps> = ({ data }) => {
}); });
const { subpage } = useContext(SubpageContext); const { subpage } = useContext(SubpageContext);
const [showDeviceEnrolled, setShowDeviceEnrolled] = useState(false);
useEffect(() => {
if (data.page === "DeviceEnrolled") {
setShowDeviceEnrolled(true);
} else {
setShowDeviceEnrolled(false);
}
}, [data.page]);
function handleCloseDeviceEnrolled() {
setShowDeviceEnrolled(false);
}
return ( return (
<Container maxWidth={false}> <Container maxWidth={false}>
<Dialog open={showDeviceEnrolled} onClose={handleCloseDeviceEnrolled}>
<DialogTitle>Device Enrolled</DialogTitle>
<DialogContent>
<DialogContentText>Device Successfully Enrolled</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseDeviceEnrolled}>OK</Button>
</DialogActions>
</Dialog>
{mdUp && ( {mdUp && (
<Drawer <Drawer
anchor="left" anchor="left"

View file

@ -1,4 +1,4 @@
import React, {createContext, FC, useState} from 'react' import React, { createContext, FC, useState } from "react";
export interface SubpageContextValue { export interface SubpageContextValue {
subpage: string; subpage: string;
@ -10,22 +10,25 @@ export const SubpageContext = createContext<SubpageContextValue>({
setSubpage: (_: string) => {}, setSubpage: (_: string) => {},
}); });
export const SubpageContextProvider:FC = ({children}) => { export type SubpageContextProviderProps = {
page: string;
};
export const SubpageContextProvider: FC<SubpageContextProviderProps> = ({
page,
children,
}) => {
const setSubpage = (subpage: string) => { const setSubpage = (subpage: string) => {
setState({...state, subpage}) setState({ ...state, subpage });
} };
const initState = { const initState = {
subpage: "User", subpage: page === "DeviceEnrolled" ? "Devices Info" : "User",
setSubpage setSubpage,
} };
const [state, setState] = useState(initState) const [state, setState] = useState(initState);
return ( return (
<SubpageContext.Provider value={state}> <SubpageContext.Provider value={state}>{children}</SubpageContext.Provider>
{children} );
</SubpageContext.Provider> };
)
}

View file

@ -95,18 +95,7 @@ export type ErrorPageData = BasePageData & {
version?: string; version?: string;
}; };
export type DeviceEnrolledPageData = BasePageData & { export type UserInfoData = {
page: "DeviceEnrolled";
};
export type SignOutConfirmPageData = BasePageData & {
page: "SignOutConfirm";
url: string;
};
export type UserInfoPageData = BasePageData & {
page: "UserInfo";
csrfToken: string; csrfToken: string;
directoryGroups?: Group[]; directoryGroups?: Group[];
directoryUser?: DirectoryUser; directoryUser?: DirectoryUser;
@ -118,6 +107,21 @@ export type UserInfoPageData = BasePageData & {
pomeriumVersion: string; pomeriumVersion: string;
}; };
export type DeviceEnrolledPageData = BasePageData &
UserInfoData & {
page: "DeviceEnrolled";
};
export type SignOutConfirmPageData = BasePageData & {
page: "SignOutConfirm";
url: string;
};
export type UserInfoPageData = BasePageData &
UserInfoData & {
page: "UserInfo";
};
export type WebAuthnRegistrationPageData = BasePageData & { export type WebAuthnRegistrationPageData = BasePageData & {
page: "WebAuthnRegistration"; page: "WebAuthnRegistration";