Skip to content

Commit

Permalink
feat: manage application scopes and realm groups
Browse files Browse the repository at this point in the history
  • Loading branch information
ymarcon committed Jan 3, 2025
1 parent 633b538 commit 3223da9
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 15 deletions.
102 changes: 102 additions & 0 deletions agate-ui/src/components/ApplicationDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,71 @@
<div class="text-hint q-mb-md">
{{ t('application.auto_approval_hint') }}
</div>
<div class="q-mb-md">
<div>{{ t('application.scopes') }}</div>
<div class="text-hint">{{ t('application.scopes_hint') }}</div>
<q-list>
<q-item v-for="scope in selected.scopes" :key="scope.name" clickable>
<q-item-section>
<div class="row q-col-gutter-md">
<q-input v-model="scope.name" :label="t('name') + ' *'" :hint="t('name_hint')" dense />
<q-input v-model="scope.description" :label="t('description')" dense />
</div>
</q-item-section>
<q-item-section side>
<q-btn flat dense icon="delete" size="sm" color="negative" @click="removeScope(scope.name)" />
</q-item-section>
</q-item>
</q-list>
<q-btn
icon="add"
no-caps
color="primary"
size="sm"
:label="t('application.add_scope')"
@click="onAddScope"
class="q-mt-md" />
</div>
<div class="q-mb-md">
<div>{{ t('application.realms_groups') }}</div>
<div class="text-hint">{{ t('application.realms_groups_hint') }}</div>
<q-list>
<q-item v-for="realmGroup in selected.realmGroups" :key="realmGroup.realm" clickable>
<q-item-section>
<div class="row q-col-gutter-md">
<q-select
v-model="realmGroup.realm"
:label="t('realm')"
:options="realmOptions"
emit-value
map-options
dense />
<q-select
v-model="realmGroup.groups"
:label="t('groups')"
:options="groupOptions"
multiple
emit-value
map-options
use-chips
dense
style="min-width: 200px;" />
</div>
</q-item-section>
<q-item-section side>
<q-btn flat dense icon="delete" size="sm" color="negative" @click="removeRealmGroups(realmGroup.realm)" />
</q-item-section>
</q-item>
</q-list>
<q-btn
icon="add"
no-caps
color="primary"
size="sm"
:label="t('application.add_realm_groups')"
@click="onAddRealmGroups"
class="q-mt-md" />
</div>
</q-form>
</q-card-section>

Expand All @@ -90,6 +155,8 @@ import { copyToClipboard } from 'quasar';
const { t } = useI18n();
const applicationStore = useApplicationStore();
const groupStore = useGroupStore();
const realmStore = useRealmStore();
interface DialogProps {
modelValue: boolean;
Expand All @@ -104,6 +171,10 @@ const selected = ref<ApplicationDto>(props.application ?? ({ autoApproval: true
const editMode = ref(false);
const key = ref('');
const groupOptions = computed(() => groupStore.groups?.map((group) => ({ label: group.name, value: group.id })) ?? []);
const realmOptions = computed(() =>
realmStore.realms?.map((realm) => ({ label: realm.name, value: realm.id || '' })) ?? []
);
const isValid = computed(
() =>
selected.value.name &&
Expand All @@ -113,6 +184,8 @@ const isValid = computed(
onMounted(() => {
applicationStore.init();
groupStore.init();
realmStore.init();
});
watch(
Expand Down Expand Up @@ -162,4 +235,33 @@ function copyKeyToClipboard() {
});
}
}
function removeScope(name: string) {
selected.value.scopes = selected.value.scopes.filter((s) => s.name !== name);
}
function onAddScope() {
if (!selected.value.scopes) {
selected.value.scopes = [];
}
selected.value.scopes.push({ name: 'scope-' + (selected.value.scopes.length + 1) });
}
function removeRealmGroups(realm: string) {
selected.value.realmGroups = selected.value.realmGroups.filter((rg) => rg.realm !== realm);
}
function onAddRealmGroups() {
if (!realmOptions.value.length) {
return;
}
if (!selected.value.realmGroups) {
selected.value.realmGroups = [];
}
const realm = realmOptions.value.find((rlm) => !selected.value.realmGroups.map((rlmGrps) => rlmGrps.realm).includes(rlm.value))?.value || '';
if (!realm) {
return;
}
selected.value.realmGroups.push({ realm, groups: [] });
}
</script>
16 changes: 10 additions & 6 deletions agate-ui/src/components/ApplicationsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,14 @@
<q-td key="description" :props="props">
<span>{{ props.row.description }}</span>
</q-td>
<q-td key="applications" :props="props">
<template v-for="app in props.row.applications" :key="app">
<q-badge :label="getApplicationName(app)" class="on-left" />
<q-td key="scopes" :props="props">
<template v-for="scope in props.row.scopes" :key="scope.name">
<q-badge :label="scope.name" :title="scope.description" class="on-left" />
</template>
</q-td>
<q-td key="realmGroups" :props="props">
<template v-for="realmGroups in props.row.realmGroups" :key="realmGroups.realm">
<q-badge :label="realmGroups.realm" :title="realmGroups.groups.join(', ')" class="on-left" />
</template>
</q-td>
</q-tr>
Expand Down Expand Up @@ -95,6 +100,8 @@ const columns = computed(() => [
{ name: 'id', label: 'ID', field: 'id', align: DefaultAlignment },
{ name: 'name', label: t('name'), field: 'name', align: DefaultAlignment },
{ name: 'description', label: t('description'), field: 'description', align: DefaultAlignment },
{ name: 'scopes', label: t('application.scopes'), field: 'scopes', align: DefaultAlignment },
{ name: 'realmGroups', label: t('application.realms_groups'), field: 'realmGroups', align: DefaultAlignment },
]);
onMounted(() => {
Expand Down Expand Up @@ -137,7 +144,4 @@ function onSaved() {
refresh();
}
function getApplicationName(id: string) {
return applicationStore.applications?.find((app) => app.id === id)?.name;
}
</script>
6 changes: 6 additions & 0 deletions agate-ui/src/i18n/en/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export default {
key_copied: 'Key copied',
auto_approval: 'User approved on sign up',
auto_approval_hint: 'Automatically approve a user who signed up through the application. Otherwise the user will be in "Pending" state, requiring manual approval.',
scopes: 'Permissions',
scopes_hint: 'Permissions allow to qualify the authorization access to the application that is granted in the OAuth context. Permissions are optional.',
add_scope: 'Add permission',
realms_groups: 'Realms and groups',
realms_groups_hint: 'Mapping between realm and group names. When defined, corresponding realms will be proposed for user signin/signup from the application. When a user joins though an application, using a realm, the corresponding group(s) will be automatically applied.',
add_realm_groups: 'Add realm groups',
},
group: {
add: 'Add group',
Expand Down
6 changes: 6 additions & 0 deletions agate-ui/src/i18n/fr/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export default {
key_copied: 'Clé copiée',
auto_approval: "Usager approuvé à l'inscription",
auto_approval_hint: "Approuver automatiquement un usager qui s'est inscrit via l'application. Sinon, l'usager sera dans l'état \"En attente\", nécessitant une approbation manuelle.",
scopes: 'Permissions',
scopes_hint: 'Les permissions permettent de qualifier l\'accès d\'autorisation à l\'application qui est accordé dans le contexte OAuth. Les permissions sont optionnelles.',
add_scope: 'Ajouter une permission',
realms_groups: 'Domaines et groupes',
realms_groups_hint: 'Mapping entre des domaines et des groupes. Lorsque défini, les domaines correspondants seront proposés pour la connexion/inscription de l\'usager à partir de l\'application. Lorsqu\'un usager rejoint une application, en utilisant un domaine, le(s) groupe(s) correspondant(s) seront automatiquement appliqués.',
add_realm_groups: 'Ajouter un domaine',
},
group: {
add: 'Ajouter un groupe',
Expand Down
26 changes: 17 additions & 9 deletions agate-ui/src/models/Agate.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3223da9

Please sign in to comment.