diff --git a/lib/nile/src/Nile.ts b/lib/nile/src/Nile.ts index aa2b4d22..a2bb3bd9 100644 --- a/lib/nile/src/Nile.ts +++ b/lib/nile/src/Nile.ts @@ -6,14 +6,19 @@ import { AccessApi, DevelopersApi, EntitiesApi, - OrganizationsApi, + OrganizationsApi as OrgApi, UsersApi, - WorkspacesApi, + WorkspacesApi as WorkspaceApi, MetricsApi, } from './client/src'; import { Configuration, ConfigurationParameters } from './client/src/runtime'; import EventsApi from './EventsApi'; import { AuthToken, DeveloperCredentials } from './model/DeveloperCredentials'; +import { OrgProviders, organizationProviders } from './OrganizationsOidc'; +import { SpaceProviders, workspaceProviders } from './WorkspaceOidc'; + +type OrganizationsApi = OrgApi & OrgProviders; +type WorkspacesApi = WorkspaceApi & SpaceProviders; /** * The base nile class. Pulls together groups of OpenAPI spec into a single class @@ -30,11 +35,26 @@ export class NileApi { metrics: MetricsApi; constructor(configuration?: Configuration) { this.config = configuration; + + // tack on org oidc + const orgsProviders = organizationProviders( + this.config?.basePath, + this.config?.workspace + ); + this.organizations = new OrgApi(configuration) as OrganizationsApi; + this.organizations.oidc = orgsProviders; + + // tack on workspace oidc + const worksProviders = workspaceProviders( + this.config?.basePath, + this.config?.workspace + ); + this.workspaces = new WorkspaceApi(configuration) as WorkspacesApi; + this.workspaces.oidc = worksProviders; + this.users = new UsersApi(configuration); this.developers = new DevelopersApi(configuration); this.entities = new EntitiesApi(configuration); - this.workspaces = new WorkspacesApi(configuration); - this.organizations = new OrganizationsApi(configuration); this.events = new EventsApi(this.entities); this.access = new AccessApi(configuration); this.metrics = new MetricsApi(configuration); @@ -45,11 +65,23 @@ export class NileApi { */ set workspace(workspace: void | string) { if (workspace) { + const orgsProviders = organizationProviders( + this.config?.basePath, + workspace + ); + this.organizations.oidc = orgsProviders; + + const worksProviders = workspaceProviders( + this.config?.basePath, + workspace + ); + this.workspaces.oidc = worksProviders; + + this.organizations.workspace = workspace; this.users.workspace = workspace; this.developers.workspace = workspace; this.entities.workspace = workspace; this.workspaces.workspace = workspace; - this.organizations.workspace = workspace; this.access.workspace = workspace; this.metrics.workspace = workspace; } diff --git a/lib/nile/src/OrganizationsOidc.ts b/lib/nile/src/OrganizationsOidc.ts new file mode 100644 index 00000000..460c0d17 --- /dev/null +++ b/lib/nile/src/OrganizationsOidc.ts @@ -0,0 +1,37 @@ +import { OidcOrganizationLoginProviderNameEnum } from './client/src'; + +type ProviderFn = (org: string) => string; + +export type OrgProviders = { + oidc: OrgaizationsIdentityProviders; +}; + +export type OrgaizationsIdentityProviders = { + [provider in OidcOrganizationLoginProviderNameEnum]: ProviderFn; +}; + +/** + * Usecase for this would be an 'invite' flow, where you want to have a user + * sign up and be added to inviter org + * @param workspace + * @returns a dictionary of urls to navigate to for redirection + */ +export const organizationProviders = (basePath?: string, workspace?: string) => + Object.values(OidcOrganizationLoginProviderNameEnum).reduce( + (accum: OrgaizationsIdentityProviders, providerName) => { + accum[providerName] = (org: string) => + [ + basePath, + 'workspaces', + workspace, + 'orgs', + org, + 'oidc', + 'providers', + providerName, + 'login', + ].join('/'); + return accum; + }, + {} as OrgaizationsIdentityProviders + ); diff --git a/lib/nile/src/WorkspaceOidc.ts b/lib/nile/src/WorkspaceOidc.ts new file mode 100644 index 00000000..f4396985 --- /dev/null +++ b/lib/nile/src/WorkspaceOidc.ts @@ -0,0 +1,34 @@ +import { OidcWorkspaceLoginProviderNameEnum } from './client/src'; + +type ProviderFn = () => string; + +export type SpaceProviders = { + oidc: WorkspaceIdentityProviders; +}; + +export type WorkspaceIdentityProviders = { + [provider in OidcWorkspaceLoginProviderNameEnum]: ProviderFn; +}; + +/** + * Usecase for this would be an 'login' flow, a user signs up to the Nile app + * @param workspace + * @returns a dictionary of urls to navigate to for redirection + */ +export const workspaceProviders = (basePath?: string, workspace?: string) => + Object.values(OidcWorkspaceLoginProviderNameEnum).reduce( + (accum: WorkspaceIdentityProviders, providerName) => { + accum[providerName] = () => + [ + basePath, + 'workspaces', + workspace, + 'oidc', + 'providers', + providerName, + 'login', + ].join('/'); + return accum; + }, + {} as WorkspaceIdentityProviders + ); diff --git a/lib/nile/src/test/index.test.ts b/lib/nile/src/test/index.test.ts index a536d7b2..4d2aae59 100644 --- a/lib/nile/src/test/index.test.ts +++ b/lib/nile/src/test/index.test.ts @@ -13,11 +13,11 @@ describe('index', () => { const keys = Object.keys(nile); expect(keys).toEqual([ 'config', + 'organizations', + 'workspaces', 'users', 'developers', 'entities', - 'workspaces', - 'organizations', 'events', 'access', 'metrics', @@ -29,6 +29,7 @@ describe('index', () => { Object.getPrototypeOf(nile[k]) ); if (k === 'workspaces') { + expect(nile[k].oidc).toBeTruthy(); expect(props).toEqual([ 'constructor', 'createAccessToken', @@ -46,6 +47,7 @@ describe('index', () => { ]); } if (k === 'organizations') { + expect(nile[k].oidc).toBeTruthy(); expect(props).toEqual([ 'constructor', 'acceptInvite', @@ -244,4 +246,20 @@ describe('index', () => { expect(nile.workspaces.workspace).toBe('123'); expect(nile.organizations.workspace).toBe('123'); }); + + describe('oidc', () => { + it('sets the correct oidc workspace url', () => { + const nile = Nile({ workspace: '123' }); + expect(nile.workspaces.oidc.GOOGLE()).toEqual( + 'http://localhost:8080/workspaces/123/oidc/providers/GOOGLE/login' + ); + }); + + it('sets the correct oidc organization url', () => { + const nile = Nile({ workspace: '123' }); + expect(nile.organizations.oidc.GOOGLE('myorg')).toEqual( + 'http://localhost:8080/workspaces/123/orgs/myorg/oidc/providers/GOOGLE/login' + ); + }); + }); }); diff --git a/packages/react/src/components/LoginForm/GoogleLoginButton.tsx b/packages/react/src/components/LoginForm/GoogleLoginButton.tsx index 5891b790..fa69085e 100644 --- a/packages/react/src/components/LoginForm/GoogleLoginButton.tsx +++ b/packages/react/src/components/LoginForm/GoogleLoginButton.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Button, Typography, Box, Stack } from '@mui/joy'; -import { OidcWorkspaceLoginProviderNameEnum } from '@theniledev/js'; import { useNile } from '../../context'; @@ -11,24 +10,10 @@ import GoogleLogo from './google.svg'; export default function LogInGoogle() { const nile = useNile(); - const href = React.useMemo( - () => - [ - nile.config?.basePath, - 'workspaces', - nile.workspace, - 'oidc', - 'providers', - OidcWorkspaceLoginProviderNameEnum.Google, - 'login', - ].join('/'), - [nile.config?.basePath, nile.workspace] - ); - return (