Skip to content

Commit

Permalink
[admin panel] Add delete user button
Browse files Browse the repository at this point in the history
  • Loading branch information
corneliusludmann committed Jan 13, 2021
1 parent 8ca431f commit 225e102
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 7 deletions.
67 changes: 60 additions & 7 deletions components/dashboard/ee/src/components/admin/user-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { SelectRoleOrPermissionDialog } from './select-role-dialog';
import { SelectWorkspaceFeatureFlagDialog } from './select-feature-flag-dialog';
import { ResponseError } from 'vscode-jsonrpc';
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
import { Dialog, DialogTitle, DialogContent, TextField, DialogActions } from '@material-ui/core';

export interface UserViewProps {
service: GitpodService;
Expand All @@ -36,6 +37,8 @@ interface UserViewState {
blockingOp: boolean;
addingRoleOp: 'none' | 'choose' | 'working';
addingFeatureFlagOp: 'none' | 'choose' | 'working';
deletingOp: 'none' | 'confirmation' | 'working' | 'done';
deletingConfirmedUsername: string;
}

interface DetailRowSpec {
Expand All @@ -51,16 +54,20 @@ export class UserView extends React.Component<UserViewProps, UserViewState> {
this.state = {
blockingOp: false,
addingRoleOp: 'none',
addingFeatureFlagOp: 'none'
addingFeatureFlagOp: 'none',
deletingOp: 'none',
deletingConfirmedUsername: '',
};
}

async componentDidMount() {
await this.fetchUser();
}

protected async fetchUser() {
try {
const loggedInUser = await this.props.service.server.getLoggedInUser();
const [ user ] = await Promise.all([
this.props.service.server.adminGetUser(this.props.userID)
]);
const user = await this.props.service.server.adminGetUser(this.props.userID);
this.setState({user, ourself: loggedInUser.id === this.props.userID});
} catch (err) {
var rerr: ResponseError<any> = err;
Expand Down Expand Up @@ -150,6 +157,29 @@ export class UserView extends React.Component<UserViewProps, UserViewState> {
open={this.state.addingFeatureFlagOp === 'choose'}
onSelect={r => this.modifyWorkspaceFeatureFlags(r, true)}
/>
<Dialog open={this.state.deletingOp == 'confirmation'}>
<React.Fragment>
<DialogTitle>Delete User: Are your sure?</DialogTitle>
<DialogContent>
<Typography variant="body1" style={{ width: "100%" }}>
<div>Are you sure that you would like to delete user <strong>{this.state.user?.name}</strong>?</div>
<div>
Please type in the username <strong>{this.state.user?.name}</strong> to confirm: <br />
<TextField value={this.state.deletingConfirmedUsername} onChange={e => this.setState({ deletingConfirmedUsername: (e.target as HTMLInputElement).value })} />
</div>
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({ deletingOp: 'none' })} variant="outlined" color="primary">Cancel</Button>
<Button
onClick={this.deleteUser} variant="outlined"
color="secondary" disabled={this.state.user?.name != this.state.deletingConfirmedUsername}
>
I'm Sure. Delete Account!
</Button>
</DialogActions>
</React.Fragment>
</Dialog>
{ user &&
<Grid container>
<Grid item xs={1}>
Expand All @@ -161,12 +191,19 @@ export class UserView extends React.Component<UserViewProps, UserViewState> {
marginLeft: 20
}}
data-testid={"avatar-" + user.id}>
</Avatar>
</Avatar>
</Grid>
<Grid item xs={11}>
<Grid item xs={9}>
<Typography variant="h1">{user.name}</Typography>
</Grid>
{/* { !this.state.ourself && <Grid item xs={2} style={{textAlign: "right"}}><Button color="secondary" variant="contained">Delete</Button></Grid>} */}
<Grid item xs={2} style={{ textAlign: "right" }}>
<Button color="secondary" variant="contained"
disabled={this.state.ourself || this.state.deletingOp !== 'none' || this.state.user?.markedDeleted}
onClick={() => this.setState({ deletingOp: 'confirmation' })}
>
Delete
</Button>
</Grid>
</Grid>
}
{ !user && <div className="loading-skeleton dummy" style={{ minWidth: "20em", minHeight: "10em" }} /> }
Expand Down Expand Up @@ -244,4 +281,20 @@ export class UserView extends React.Component<UserViewProps, UserViewState> {
}
}

private deleteUser = async () => {
this.setState({ deletingOp: 'working' });
try {
if (!this.state.user) {
throw new Error("User is undefined. That was unexpected.");
}
await this.props.service.server.adminDeleteUser(this.state.user.id);
await this.fetchUser();
} catch (err) {
console.error("Error deleting user: " + err);
alert(err);
} finally {
this.setState({ deletingOp: 'done' });
}
}

}
1 change: 1 addition & 0 deletions components/gitpod-protocol/src/admin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface AdminServer {
adminGetUsers(req: AdminGetListRequest<User>): Promise<AdminGetListResult<User>>;
adminGetUser(id: string): Promise<User>;
adminBlockUser(req: AdminBlockUserRequest): Promise<User>;
adminDeleteUser(id: string): Promise<void>;
adminModifyRoleOrPermission(req: AdminModifyRoleOrPermissionRequest): Promise<User>;
adminModifyPermanentWorkspaceFeatureFlag(req: AdminModifyPermanentWorkspaceFeatureFlagRequest): Promise<User>;

Expand Down
19 changes: 19 additions & 0 deletions components/server/ee/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,25 @@ export class GitpodServerEEImpl<C extends GitpodClient, S extends GitpodServer>
}
}

async adminDeleteUser(id: string): Promise<void> {
this.requireEELicense(Feature.FeatureAdminDashboard);

const user = this.checkAndBlockUser("adminDeleteUser");
if (!this.authorizationService.hasPermission(user, Permission.ADMIN_USERS)) {
throw new ResponseError(ErrorCodes.PERMISSION_DENIED, "not allowed");
}

const span = opentracing.globalTracer().startSpan("adminDeleteUser");
try {
await this.userDeletionService.deleteUser(id);
} catch (e) {
TraceContext.logError({ span }, e);
throw new ResponseError(500, e.toString());
} finally {
span.finish();
}
}

async adminModifyRoleOrPermission(req: AdminModifyRoleOrPermissionRequest): Promise<User> {
this.requireEELicense(Feature.FeatureAdminDashboard);

Expand Down
4 changes: 4 additions & 0 deletions components/server/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,10 @@ export class GitpodServerImpl<Client extends GitpodClient, Server extends Gitpod
throw new ResponseError(ErrorCodes.EE_FEATURE, `Admin support is implemented in Gitpod's Enterprise Edition`);
}

async adminDeleteUser(_id: string): Promise<void> {
throw new ResponseError(ErrorCodes.EE_FEATURE, `Admin support is implemented in Gitpod's Enterprise Edition`);
}

async adminModifyRoleOrPermission(req: AdminModifyRoleOrPermissionRequest): Promise<User> {
throw new ResponseError(ErrorCodes.EE_FEATURE, `Admin support is implemented in Gitpod's Enterprise Edition`);
}
Expand Down

0 comments on commit 225e102

Please sign in to comment.