authenticate: update user info dashboard to show group info for enterprise (#3736)

* authenticate: update user info dashboard to show group info for enterprise

* Update ui/src/components/GroupDetails.tsx

Co-authored-by: bobby <1544881+desimone@users.noreply.github.com>

Co-authored-by: bobby <1544881+desimone@users.noreply.github.com>
This commit is contained in:
Caleb Doxsey 2022-11-09 07:44:35 -07:00 committed by GitHub
parent 45ce6f693a
commit 2b319822a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 118 additions and 26 deletions

View file

@ -17,6 +17,7 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/pomerium/csrf"
"github.com/pomerium/datasource/pkg/directory"
"github.com/pomerium/pomerium/authenticate/handlers"
"github.com/pomerium/pomerium/authenticate/handlers/webauthn"
"github.com/pomerium/pomerium/internal/httputil"
@ -545,7 +546,7 @@ func (a *Authenticate) getUserInfoData(r *http.Request) (handlers.UserInfoData,
}
creationOptions, requestOptions, _ := a.webauthn.GetOptions(r.Context())
return handlers.UserInfoData{
data := handlers.UserInfoData{
CSRFToken: csrf.Token(r),
IsImpersonated: isImpersonated,
Session: pbSession,
@ -556,7 +557,33 @@ func (a *Authenticate) getUserInfoData(r *http.Request) (handlers.UserInfoData,
WebAuthnURL: urlutil.WebAuthnURL(r, authenticateURL, state.sharedKey, r.URL.Query()),
BrandingOptions: a.options.Load().BrandingOptions,
}, nil
}
a.fillEnterpriseUserInfoData(r.Context(), &data, pbSession.GetUserId())
return data, nil
}
func (a *Authenticate) fillEnterpriseUserInfoData(
ctx context.Context,
dst *handlers.UserInfoData,
userID string,
) {
client := a.state.Load().dataBrokerClient
res, _ := client.Get(ctx, &databroker.GetRequest{Type: "type.googleapis.com/pomerium.config.Config", Id: "dashboard"})
dst.IsEnterprise = res.GetRecord() != nil
if !dst.IsEnterprise {
return
}
dst.DirectoryUser, _ = databroker.GetViaJSON[directory.User](ctx, client, directory.UserRecordType, userID)
if dst.DirectoryUser != nil {
for _, groupID := range dst.DirectoryUser.GroupIDs {
directoryGroup, _ := databroker.GetViaJSON[directory.Group](ctx, client, directory.GroupRecordType, groupID)
if directoryGroup != nil {
dst.DirectoryGroups = append(dst.DirectoryGroups, directoryGroup)
}
}
}
}
func (a *Authenticate) saveSessionToDataBroker(

View file

@ -6,6 +6,7 @@ import (
"google.golang.org/protobuf/encoding/protojson"
"github.com/pomerium/datasource/pkg/directory"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/pkg/grpc/session"
"github.com/pomerium/pomerium/pkg/grpc/user"
@ -20,6 +21,10 @@ type UserInfoData struct {
Session *session.Session
User *user.User
IsEnterprise bool
DirectoryUser *directory.User
DirectoryGroups []*directory.Group
WebAuthnCreationOptions *webauthn.PublicKeyCredentialCreationOptions
WebAuthnRequestOptions *webauthn.PublicKeyCredentialRequestOptions
WebAuthnURL string
@ -38,6 +43,13 @@ func (data UserInfoData) ToJSON() map[string]any {
if bs, err := protojson.Marshal(data.User); err == nil {
m["user"] = json.RawMessage(bs)
}
m["isEnterprise"] = data.IsEnterprise
if data.DirectoryUser != nil {
m["directoryUser"] = data.DirectoryUser
}
if len(data.DirectoryGroups) > 0 {
m["directoryGroups"] = data.DirectoryGroups
}
m["webAuthnCreationOptions"] = data.WebAuthnCreationOptions
m["webAuthnRequestOptions"] = data.WebAuthnRequestOptions
m["webAuthnUrl"] = data.WebAuthnURL

1
go.mod
View file

@ -46,6 +46,7 @@ require (
github.com/ory/dockertest/v3 v3.9.1
github.com/peterbourgon/ff/v3 v3.3.0
github.com/pomerium/csrf v1.7.0
github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524
github.com/pomerium/webauthn v0.0.0-20211014213840-422c7ce1077f
github.com/prometheus/client_golang v1.13.1
github.com/prometheus/client_model v0.3.0

2
go.sum
View file

@ -789,6 +789,8 @@ github.com/polyfloyd/go-errorlint v1.0.5 h1:AHB5JRCjlmelh9RrLxT9sgzpalIwwq4hqE8E
github.com/polyfloyd/go-errorlint v1.0.5/go.mod h1:APVvOesVSAnne5SClsPxPdfvZTVDojXh1/G3qb5wjGI=
github.com/pomerium/csrf v1.7.0 h1:Qp4t6oyEod3svQtKfJZs589mdUTWKVf7q0PgCKYCshY=
github.com/pomerium/csrf v1.7.0/go.mod h1:hAPZV47mEj2T9xFs+ysbum4l7SF1IdrryYaY6PdoIqw=
github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 h1:3YQY1sb54tEEbr0L73rjHkpLB0IB6qh3zl1+XQbMLis=
github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524/go.mod h1:7fGbUYJnU8RcxZJvUvhukOIBv1G7LWDAHMfDxAf5+Y0=
github.com/pomerium/webauthn v0.0.0-20211014213840-422c7ce1077f h1:442shkoI4Oh4RHdzFaGma1t9Ji/T+8pfCxQQzmY5kj8=
github.com/pomerium/webauthn v0.0.0-20211014213840-422c7ce1077f/go.mod h1:wgH3ualWdXu/qwbhOoSQedXzco+38Iz7qKKGCJcKPXg=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=

View file

@ -3,9 +3,11 @@ package databroker
import (
"context"
"encoding/json"
"fmt"
"io"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
structpb "google.golang.org/protobuf/types/known/structpb"
@ -43,6 +45,34 @@ func Get(ctx context.Context, client DataBrokerServiceClient, object recordObjec
return res.GetRecord().GetData().UnmarshalTo(object)
}
// GetViaJSON gets a record from the databroker, marshals it to JSON, and then unmarshals it to the given type.
func GetViaJSON[T any](ctx context.Context, client DataBrokerServiceClient, recordType, recordID string) (*T, error) {
res, err := client.Get(ctx, &GetRequest{
Type: recordType,
Id: recordID,
})
if err != nil {
return nil, err
}
msg, err := res.GetRecord().GetData().UnmarshalNew()
if err != nil {
return nil, err
}
bs, err := protojson.Marshal(msg)
if err != nil {
return nil, err
}
var obj T
err = json.Unmarshal(bs, &obj)
if err != nil {
return nil, err
}
return &obj, nil
}
// Put puts a record into the databroker.
func Put(ctx context.Context, client DataBrokerServiceClient, objects ...recordObject) (*PutResponse, error) {
records := make([]*Record, len(objects))

View file

@ -1,6 +1,4 @@
import { Group } from "../types";
import IDField from "./IDField";
import Section from "./Section";
import Alert from "@mui/material/Alert";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
@ -9,12 +7,21 @@ import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import React, { FC } from "react";
import { Group } from "../types";
import IDField from "./IDField";
import Section from "./Section";
export type GroupDetailsProps = {
isEnterprise: boolean;
groups: Group[];
};
export const GroupDetails: FC<GroupDetailsProps> = ({ groups }) => {
export const GroupDetails: FC<GroupDetailsProps> = ({
isEnterprise,
groups,
}) => {
return (
<Section title="Groups">
{isEnterprise ? (
<TableContainer>
<Table size="small">
<TableHead>
@ -35,6 +42,15 @@ export const GroupDetails: FC<GroupDetailsProps> = ({ groups }) => {
</TableBody>
</Table>
</TableContainer>
) : (
<Alert severity="info">
Groups via directory sync are available in{" "}
<a href="https://www.pomerium.com/enterprise-sales/">
Pomerium Enterprise
</a>
.
</Alert>
)}
</Section>
);
};

View file

@ -84,7 +84,10 @@ const UserInfoPage: FC<UserInfoPageProps> = ({ data }) => {
{subpage === "User" && <SessionDetails session={data?.session} />}
{subpage === "Groups Info" && (
<GroupDetails groups={data?.directoryGroups} />
<GroupDetails
isEnterprise={data?.isEnterprise}
groups={data?.directoryGroups}
/>
)}
{subpage === "Devices Info" && (

View file

@ -103,6 +103,7 @@ export type UserInfoData = {
csrfToken: string;
directoryGroups?: Group[];
directoryUser?: DirectoryUser;
isEnterprise?: boolean;
session?: Session;
user?: User;
webAuthnCreationOptions?: WebAuthnCreationOptions;