Skip to content

Commit

Permalink
feat: keycloak user/group sync (#296)
Browse files Browse the repository at this point in the history
  • Loading branch information
hairmare authored Feb 16, 2024
1 parent 0601885 commit 06fcb1d
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 1 deletion.
14 changes: 14 additions & 0 deletions app-config.production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,17 @@ catalog:
# See https://backstage.io/docs/features/software-catalog/software-catalog-overview#adding-components-to-the-catalog for more details
# on how to get entities into the catalog.
locations: []
providers:
keycloakOrg:
production:
baseUrl: ${CATALOG_KEYCLOAK_BASEURL}
loginRealm: ${CATALOG_KEYCLOAK_LOGINREALM}
realm: ${CATALOG_KEYCLOAK_REALM}
clientId: ${CATALOG_KEYCLOAK_CLIENT_ID}
clientSecret: ${CATALOG_KEYCLOAK_CLIENT_SECRET}
schedule:
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
timeout: { minutes: 1 }
initialDelay: { seconds: 15 }
42 changes: 42 additions & 0 deletions packages/backend/src/plugins/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,38 @@ import { Router } from 'express';
import { PluginEnvironment } from '../types';

import { GithubEntityProvider } from '@backstage/plugin-catalog-backend-module-github';
// @ts-ignore
import { GroupTransformer, UserTransformer, KeycloakOrgEntityProvider } from '@janus-idp/backstage-plugin-keycloak-backend';

// todo: move this to config if it starts growing
const GROUP_ALLOW_REGEX = /(it-reaktion|webteam)/

const userTransformer: UserTransformer = async (
entity: any,
) => {
// for data economy reasons we only keep relevant groups
entity.spec.memberOf = entity.spec.memberOf.filter((x: string) => x.match(GROUP_ALLOW_REGEX))
// this lets us remove users that do not need to have a profile in backstage
if (entity.spec.memberOf.length == 0) return null
return entity;
};

const groupTransformer: GroupTransformer = async (
entity: any,
) => {
// for data economy reasons we only create relevant groups
if (!entity.metadata.name.match(GROUP_ALLOW_REGEX)) return null
// hax: we want a nice link so we hardcode most of it :)
entity.metadata.links = [
{
title: "IPA",
// todo: un-hardcode this uwu
url: `https://ipa-01.service.int.rabe.ch/ipa/ui/#/e/group/details/${entity.metadata.name}`,
icon: "group"
}
]
return entity;
};

export default async function createPlugin(
env: PluginEnvironment,
Expand All @@ -17,6 +49,16 @@ export default async function createPlugin(
scheduler: env.scheduler,
}),
);
// Import group and user entities from our Keycloak federation
builder.addEntityProvider(
KeycloakOrgEntityProvider.fromConfig(env.config, {
id: 'production',
logger: env.logger,
scheduler: env.scheduler,
userTransformer,
groupTransformer,
}),
);
builder.addProcessor(new ScaffolderEntitiesProcessor());
const { processingEngine, router } = await builder.build();
const unprocessed = new UnprocessedEntitiesModule(
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/plugins/permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Router } from "express-serve-static-core";
import { PluginEnvironment } from "../types";


const BACKSTAGE_ADMINS_GROUP = 'group:default/backstage-admins';
const BACKSTAGE_ADMINS_GROUP = 'group:default/it-reaktion';

class CustomPermissionPolicy implements PermissionPolicy {
async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise<PolicyDecision> {
Expand Down

0 comments on commit 06fcb1d

Please sign in to comment.