diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 28c9b5b..309df30 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -22,12 +22,13 @@ import { import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; import { UserSettingsPage } from '@backstage/plugin-user-settings'; -import { apis } from './apis'; +import {apis} from './apis'; +import {keycloakOIDCAuthApiRef} from "@internal/plugin-workflows" import { entityPage } from './components/catalog/EntityPage'; import { searchPage } from './components/search/SearchPage'; import { Root } from './components/Root'; -import {AlertDisplay, OAuthRequestDialog, ProxiedSignInPage} from '@backstage/core-components'; +import {AlertDisplay, OAuthRequestDialog, SignInPage} from '@backstage/core-components'; import { createApp } from '@backstage/app-defaults'; import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; @@ -37,7 +38,19 @@ import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/ const app = createApp({ apis, components: { - SignInPage: (props) => , + // SignInPage: (props) => , + SignInPage: props => ( + + ), }, bindRoutes({ bind }) { bind(catalogPlugin.externalRoutes, { diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index c89753a..206598e 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -4,11 +4,26 @@ import { ScmAuth, } from '@backstage/integration-react'; import { - AnyApiFactory, - configApiRef, - createApiFactory, + AnyApiFactory, + ApiRef, + BackstageIdentityApi, + configApiRef, + createApiFactory, + createApiRef, + discoveryApiRef, + oauthRequestApiRef, + OpenIdConnectApi, + ProfileInfoApi, + SessionApi, } from '@backstage/core-plugin-api'; +import {OAuth2} from "@backstage/core-app-api"; +import {keycloakOIDCAuthApiRef} from "@internal/plugin-workflows" +// export const keycloakOIDCAuthApiRef: ApiRef< +// OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +// > = createApiRef({ +// id: 'auth.keycloak-oidc-provider', +// }); export const apis: AnyApiFactory[] = [ createApiFactory({ api: scmIntegrationsApiRef, @@ -16,4 +31,24 @@ export const apis: AnyApiFactory[] = [ factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), }), ScmAuth.createDefaultApiFactory(), + createApiFactory({ + api: keycloakOIDCAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'keycloak-oidc', + title: 'Keycloak OIDC', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), + defaultScopes: ['openid', 'profile', 'email', 'groups'], + }), + }), ]; diff --git a/packages/backend/src/plugins/auth.ts b/packages/backend/src/plugins/auth.ts index 9790887..4042c59 100644 --- a/packages/backend/src/plugins/auth.ts +++ b/packages/backend/src/plugins/auth.ts @@ -18,70 +18,91 @@ export default async function createPlugin( tokenManager: env.tokenManager, providerFactories: { ...defaultAuthProviderFactories, - oauth2Proxy: providers.oauth2Proxy.create({ + 'keycloak-oidc': providers.oidc.create({ signIn: { - async resolver({ result }, ctx) { - console.log(result) - const name = result.getHeader('x-forwarded-preferred-username'); - if (!name) { - throw new Error('Request did not contain a user'); - } - - try { - // Attempts to sign in existing user - const signedInUser = await ctx.signInWithCatalogUser({ - entityRef: { name }, - }); - - return Promise.resolve(signedInUser); - } catch (e) { - // Create stub user - const userEntityRef = stringifyEntityRef({ - kind: 'User', - name: name, - namespace: DEFAULT_NAMESPACE, - }); - return ctx.issueToken({ - claims: { - sub: userEntityRef, - ent: [userEntityRef], - }, - }); - } + resolver(info, ctx) { + const userRef = stringifyEntityRef({ + kind: 'User', + name: info.result.userinfo.sub, + namespace: DEFAULT_NAMESPACE, + }); + console.log(info.result.userinfo.groups) + return ctx.issueToken({ + claims: { + sub: userRef, // The user's own identity + ent: [userRef], // A list of identities that the user claims ownership through + }, + }); }, }, }), - // This replaces the default GitHub auth provider with a customized one. - // The `signIn` option enables sign-in for this provider, using the - // identity resolution logic that's provided in the `resolver` callback. - // - // This particular resolver makes all users share a single "guest" identity. - // It should only be used for testing and trying out Backstage. - // - // If you want to use a production ready resolver you can switch to - // the one that is commented out below, it looks up a user entity in the - // catalog using the GitHub username of the authenticated user. - // That resolver requires you to have user entities populated in the catalog, - // for example using https://backstage.io/docs/integrations/github/org - // - // There are other resolvers to choose from, and you can also create - // your own, see the auth documentation for more details: - // - // https://backstage.io/docs/auth/identity-resolver - // github: providers.github.create({ - // signIn: { - // resolver(_, ctx) { - // const userRef = 'user:default/guest'; // Must be a full entity reference - // return ctx.issueToken({ - // claims: { - // sub: userRef, // The user's own identity - // ent: [userRef], // A list of identities that the user claims ownership through - // }, - // }); - // }, - // // resolver: providers.github.resolvers.usernameMatchingUserEntityName(), - // }, - // }), }, + // providerFactories: { + // ...defaultAuthProviderFactories, + // oauth2Proxy: providers.oauth2Proxy.create({ + // signIn: { + // async resolver({ result }, ctx) { + // console.log(result) + // const name = result.getHeader('x-forwarded-preferred-username'); + // if (!name) { + // throw new Error('Request did not contain a user'); + // } + // + // try { + // // Attempts to sign in existing user + // const signedInUser = await ctx.signInWithCatalogUser({ + // entityRef: { name }, + // }); + // + // return Promise.resolve(signedInUser); + // } catch (e) { + // // Create stub user + // const userEntityRef = stringifyEntityRef({ + // kind: 'User', + // name: name, + // namespace: DEFAULT_NAMESPACE, + // }); + // return ctx.issueToken({ + // claims: { + // sub: userEntityRef, + // ent: [userEntityRef], + // }, + // }); + // } + // }, + // }, + // }), + // // This replaces the default GitHub auth provider with a customized one. + // // The `signIn` option enables sign-in for this provider, using the + // // identity resolution logic that's provided in the `resolver` callback. + // // + // // This particular resolver makes all users share a single "guest" identity. + // // It should only be used for testing and trying out Backstage. + // // + // // If you want to use a production ready resolver you can switch to + // // the one that is commented out below, it looks up a user entity in the + // // catalog using the GitHub username of the authenticated user. + // // That resolver requires you to have user entities populated in the catalog, + // // for example using https://backstage.io/docs/integrations/github/org + // // + // // There are other resolvers to choose from, and you can also create + // // your own, see the auth documentation for more details: + // // + // // https://backstage.io/docs/auth/identity-resolver + // // github: providers.github.create({ + // // signIn: { + // // resolver(_, ctx) { + // // const userRef = 'user:default/guest'; // Must be a full entity reference + // // return ctx.issueToken({ + // // claims: { + // // sub: userRef, // The user's own identity + // // ent: [userRef], // A list of identities that the user claims ownership through + // // }, + // // }); + // // }, + // // // resolver: providers.github.resolvers.usernameMatchingUserEntityName(), + // // }, + // // }), + // }, }); } diff --git a/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx b/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx index 05f5ef3..c90d2d6 100644 --- a/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx +++ b/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx @@ -5,8 +5,8 @@ import useAsync from 'react-use/lib/useAsync'; import { DiscoveryApi, - discoveryApiRef, - useApi, + discoveryApiRef, OpenIdConnectApi, fetchApiRef, + useApi, FetchApi, } from '@backstage/core-plugin-api'; // eslint-disable-next-line no-restricted-imports import {gunzipSync} from "zlib"; @@ -22,8 +22,7 @@ import { import DeleteIcon from "@material-ui/icons/Delete"; import ClearIcon from "@material-ui/icons/Clear"; import LinkOffRounded from "@material-ui/icons/LinkOffRounded"; - -const token = "..---" +import {keycloakOIDCAuthApiRef} from "../../plugin"; type TFState = { terraform_version: string @@ -75,9 +74,11 @@ export const TFTable = (props: TFTableProps) => { export const FetchTFState = () => { const apiRef = useApi(discoveryApiRef) const entity = useEntity() + const oidcApi = useApi(keycloakOIDCAuthApiRef) + const fetchApi = useApi(fetchApiRef) const secretName = `tfstate-default-${entity.entity.metadata.name}` const { value, loading, error } = useAsync((): Promise => { - return getTFState(secretName, "admin", apiRef) + return getTFState(secretName, "admin", apiRef, oidcApi, fetchApi) }) if (loading) { return @@ -125,7 +126,8 @@ type payload = { } } -async function getTFState(name: string, namespace: string, apiRef: DiscoveryApi): Promise { +async function getTFState(name: string, namespace: string, apiRef: DiscoveryApi, oidcRef: OpenIdConnectApi, fetchRef: FetchApi ): Promise { + const token = await oidcRef.getIdToken() const baseUrl = await apiRef.getBaseUrl("kubernetes") const proxyUrl = `${baseUrl}/proxy` return new Promise(async (resolve, reject) => { @@ -133,7 +135,7 @@ async function getTFState(name: string, namespace: string, apiRef: DiscoveryApi) method: 'GET', headers: { 'X-Kubernetes-Cluster': "canoe-packaging", - Authorization: `Bearer ${token}`, + 'Authorization': `Bearer ${token}`, }, }); if (resp.ok) { diff --git a/plugins/workflows/src/index.ts b/plugins/workflows/src/index.ts index cca6659..b4097a8 100644 --- a/plugins/workflows/src/index.ts +++ b/plugins/workflows/src/index.ts @@ -1 +1,2 @@ -export { workflowsPlugin, EntityWorkflowsContent } from './plugin'; + +export { workflowsPlugin, EntityWorkflowsContent, keycloakOIDCAuthApiRef } from './plugin'; diff --git a/plugins/workflows/src/plugin.ts b/plugins/workflows/src/plugin.ts index 13a9b55..0d4552d 100644 --- a/plugins/workflows/src/plugin.ts +++ b/plugins/workflows/src/plugin.ts @@ -1,4 +1,10 @@ -import { createPlugin, createRoutableExtension } from '@backstage/core-plugin-api'; +import { + ApiRef, BackstageIdentityApi, createApiRef, + createPlugin, + createRoutableExtension, + OpenIdConnectApi, + ProfileInfoApi, SessionApi +} from '@backstage/core-plugin-api'; import { rootCatalogWorkflowsRouteRef } from './routes'; @@ -17,3 +23,9 @@ export const EntityWorkflowsContent = workflowsPlugin.provide( mountPoint: rootCatalogWorkflowsRouteRef, }), ); + +export const keycloakOIDCAuthApiRef: ApiRef< + OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +> = createApiRef({ + id: 'auth.keycloak-oidc-provider', +}); \ No newline at end of file