Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit 043c6a9

Browse files
authored
feat: role based auth using graphback/keycloak-authz (#412)
1 parent d9096fd commit 043c6a9

7 files changed

Lines changed: 59 additions & 57 deletions

File tree

client/src/components/TaskList.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { Task } from './Task';
3-
import { IonList } from '@ionic/react';
3+
import { IonList, IonToast } from '@ionic/react';
44
import { useOfflineMutation } from 'react-offix-hooks';
55
import { ITask } from '../declarations';
66
import { Empty } from './Empty';
@@ -13,12 +13,17 @@ export const TaskList: React.FC<any> = ({ tasks }) => {
1313
const [updateTaskMutation] = useOfflineMutation(updateTask, mutationOptions.updateTask);
1414
const [deleteTaskMutation] = useOfflineMutation(deleteTask, mutationOptions.deleteTask);
1515

16+
const [ showToast, setShowToast ] = useState<boolean>(false);
17+
const [ errorMessage, setErrorMessage ] = useState<string>('');
18+
1619
const handleError = (error: any) => {
1720
if(error.offline) {
1821
error.watchOfflineChange();
1922
}
2023
if (error.graphQLErrors) {
2124
console.log(error.graphQLErrors);
25+
setErrorMessage(error.message);
26+
setShowToast(true);
2227
}
2328
}
2429

@@ -54,6 +59,14 @@ export const TaskList: React.FC<any> = ({ tasks }) => {
5459
})
5560
}
5661
</IonList>
62+
<IonToast
63+
isOpen={showToast}
64+
onDidDismiss={() => setShowToast(false)}
65+
message={errorMessage}
66+
position="top"
67+
color="danger"
68+
duration={2000}
69+
/>
5770
</>
5871
);
5972

server/integrations/keycloak/initKeycloak.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ async function prepareKeycloak() {
9797
console.log('creating client roles')
9898
for (let roleName of clientRoleNames) {
9999
await createClientRole(BEARER_CLIENT, roleName)
100+
await createClientRole(PUBLIC_CLIENT, roleName)
100101
}
101102

102103
console.log('creating realm roles')
@@ -107,7 +108,8 @@ async function prepareKeycloak() {
107108
// get the actual role objects from keycloak after creating them
108109
// need to get the ids that were created on them
109110
realmRoles = await getRealmRoles()
110-
clientRoles = await getClientRoles(BEARER_CLIENT)
111+
bearerClientRoles = await getClientRoles(BEARER_CLIENT)
112+
publicClientRoles = await getClientRoles(PUBLIC_CLIENT)
111113

112114
for (let user of users) {
113115
// Create a new user
@@ -116,7 +118,8 @@ async function prepareKeycloak() {
116118

117119
// Assign roles to the user
118120
await assignRealmRolesToUser(user, userIdUrl)
119-
await assignClientRolesToUser(user, BEARER_CLIENT, userIdUrl)
121+
await assignClientRolesToUser(user, BEARER_CLIENT, bearerClientRoles, userIdUrl)
122+
await assignClientRolesToUser(user, PUBLIC_CLIENT, publicClientRoles, userIdUrl)
120123
}
121124

122125
const publicInstallation = await getClientInstallation(PUBLIC_CLIENT)
@@ -162,14 +165,14 @@ async function assignRealmRolesToUser(user, userIdUrl) {
162165
}
163166
}
164167

165-
async function assignClientRolesToUser(user, client, userIdUrl) {
168+
async function assignClientRolesToUser(user, client, clientRoles, userIdUrl) {
166169
for (let roleToAssign of user.clientRoles) {
167-
console.log(`assigning client role ${roleToAssign} from client ${PUBLIC_CLIENT_NAME} on user ${user.name}`)
170+
console.log(`assigning client role ${roleToAssign} from client ${client.clientId} on user ${user.name}`)
168171
const selectedClientRole = clientRoles.find(clientRole => clientRole.name === roleToAssign)
169172
if (selectedClientRole) {
170173
await assignClientRoleToUser(userIdUrl, client, selectedClientRole)
171174
} else {
172-
console.error(`client role ${roleToAssign} does not exist on client ${PUBLIC_CLIENT_NAME}`)
175+
console.error(`client role ${roleToAssign} does not exist on client ${client.clientId}`)
173176
}
174177
}
175178
}

server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"express-session": "1.17.0",
3636
"graphql-tag": "2.10.3",
3737
"keycloak-connect": "9.0.2",
38-
"keycloak-connect-graphql": "0.4.0"
38+
"keycloak-connect-graphql": "0.4.0",
39+
"@graphback/keycloak-authz": "0.12.0"
3940
}
4041
}

server/src/AMQCrudService.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { GraphbackOperationType } from '@graphback/core'
2+
import { KeycloakCrudService } from '@graphback/keycloak-authz'
3+
4+
5+
export class AMQCRUDService extends KeycloakCrudService {
6+
protected subscriptionTopicMapping(triggerType: GraphbackOperationType, objectName: string) {
7+
// Support AMQ topic creation format
8+
return `graphql/${objectName}_${triggerType}`
9+
}
10+
}

server/src/config/auth.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { CrudServicesAuthConfig } from "@graphback/keycloak-authz"
2+
3+
export const authConfig: CrudServicesAuthConfig = {
4+
Task: {
5+
create: { roles: [] },
6+
read: { roles: [] },
7+
update: { roles: ['admin'] },
8+
delete: { roles: ['admin'] },
9+
}
10+
}

server/src/createContext.ts

Lines changed: 0 additions & 47 deletions
This file was deleted.

server/src/graphql.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import { Config } from './config/config';
99
import { ApolloServer } from "apollo-server-express";
1010
import { Express } from "express";
1111
import { buildKeycloakApolloConfig } from './auth';
12-
import { createOffixMongoCRUDRuntimeContext } from './createContext';
12+
import { createKeycloakRuntimeContext } from '@graphback/keycloak-authz';
13+
import { authConfig } from './config/auth';
14+
import { OffixMongoDBDataProvider } from '@graphback/runtime-mongo';
15+
import { AMQCRUDService } from './AMQCrudService'
1316

1417
/**
1518
* Creates Apollo server
@@ -20,7 +23,16 @@ export const createApolloServer = async function (app: Express, config: Config)
2023

2124
const typeDefs = loadSchemaFiles(join(__dirname, '/schema/')).join('\n');
2225
const schema = buildSchema(typeDefs, { assumeValid: true });
23-
const context = createOffixMongoCRUDRuntimeContext(models, schema, db, pubSub);
26+
27+
const context = createKeycloakRuntimeContext({
28+
models,
29+
schema,
30+
db,
31+
pubSub,
32+
authConfig,
33+
dataProvider: OffixMongoDBDataProvider,
34+
crudService: AMQCRUDService
35+
})
2436

2537
let apolloConfig = {
2638
typeDefs: typeDefs,

0 commit comments

Comments
 (0)