web: use generated API Client (#616)
* api: fix types for config API * api: remove broken swagger UI * admin: re-fix system task enum * events: make event optional * events: fix Schema for notification transport test * flows: use APIView for Flow Executor * core: fix schema for Metrics APIs * web: rewrite to use generated API client * web: generate API Client in CI * admin: use x_cord and y_cord to prevent yaml issues * events: fix linting errors * web: don't lint generated code * core: fix fields not being required in TypeSerializer * flows: fix missing permission_classes * web: cleanup * web: fix rendering of graph on Overview page * web: cleanup imports * core: fix missing background image filter * flows: fix flows not advancing properly * stages/*: fix warnings during get_challenge * web: send Flow response as JSON instead of FormData * web: fix styles for horizontal tabs * web: add base chart class and custom chart for application view * root: generate ts client for e2e tests * web: don't attempt to connect to websocket in selenium tests * web: fix UserTokenList not being included in the build * web: fix styling for static token list * web: fix CSRF Token missing * stages/authenticator_static: fix error when disable static tokens * core: fix display issue when updating user info * web: fix Flow executor not showing spinner when redirecting
This commit is contained in:
		| @ -4,3 +4,8 @@ node_modules | ||||
| dist | ||||
| # don't lint nyc coverage output | ||||
| coverage | ||||
| # don't lint generated code | ||||
| src/api/apis | ||||
| src/api/models | ||||
| src/api/index.ts | ||||
| src/api/runtime.ts | ||||
|  | ||||
| @ -10,6 +10,25 @@ variables: | ||||
|     branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }} | ||||
|  | ||||
| stages: | ||||
|   - stage: generate | ||||
|     jobs: | ||||
|       - job: swagger_generate | ||||
|         pool: | ||||
|           vmImage: 'ubuntu-latest' | ||||
|         steps: | ||||
|           - task: NodeTool@0 | ||||
|             inputs: | ||||
|               versionSpec: '12.x' | ||||
|             displayName: 'Install Node.js' | ||||
|           - task: CmdLine@2 | ||||
|             inputs: | ||||
|               script: | | ||||
|                 docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true | ||||
|           - task: PublishPipelineArtifact@1 | ||||
|             inputs: | ||||
|               targetPath: 'web/src/api/' | ||||
|               artifact: 'ts_swagger_client' | ||||
|               publishLocation: 'pipeline' | ||||
|   - stage: lint | ||||
|     jobs: | ||||
|       - job: eslint | ||||
| @ -20,6 +39,11 @@ stages: | ||||
|             inputs: | ||||
|               versionSpec: '12.x' | ||||
|             displayName: 'Install Node.js' | ||||
|           - task: DownloadPipelineArtifact@2 | ||||
|             inputs: | ||||
|               buildType: 'current' | ||||
|               artifactName: 'ts_swagger_client' | ||||
|               path: "web/src/api/" | ||||
|           - task: Npm@1 | ||||
|             inputs: | ||||
|               command: 'install' | ||||
| @ -37,6 +61,11 @@ stages: | ||||
|             inputs: | ||||
|               versionSpec: '12.x' | ||||
|             displayName: 'Install Node.js' | ||||
|           - task: DownloadPipelineArtifact@2 | ||||
|             inputs: | ||||
|               buildType: 'current' | ||||
|               artifactName: 'ts_swagger_client' | ||||
|               path: "web/src/api/" | ||||
|           - task: Npm@1 | ||||
|             inputs: | ||||
|               command: 'install' | ||||
| @ -56,6 +85,11 @@ stages: | ||||
|             inputs: | ||||
|               versionSpec: '12.x' | ||||
|             displayName: 'Install Node.js' | ||||
|           - task: DownloadPipelineArtifact@2 | ||||
|             inputs: | ||||
|               buildType: 'current' | ||||
|               artifactName: 'ts_swagger_client' | ||||
|               path: "web/src/api/" | ||||
|           - task: Npm@1 | ||||
|             inputs: | ||||
|               command: 'install' | ||||
| @ -71,16 +105,21 @@ stages: | ||||
|         pool: | ||||
|           vmImage: 'ubuntu-latest' | ||||
|         steps: | ||||
|         - task: Bash@3 | ||||
|           inputs: | ||||
|             targetType: 'inline' | ||||
|             script: | | ||||
|               python ./scripts/az_do_set_branch.py | ||||
|         - task: Docker@2 | ||||
|           inputs: | ||||
|             containerRegistry: 'beryjuorg-harbor' | ||||
|             repository: 'authentik/static' | ||||
|             command: 'buildAndPush' | ||||
|             Dockerfile: 'web/Dockerfile' | ||||
|             tags: "gh-$(branchName)" | ||||
|             buildContext: 'web/' | ||||
|           - task: DownloadPipelineArtifact@2 | ||||
|             inputs: | ||||
|               buildType: 'current' | ||||
|               artifactName: 'ts_swagger_client' | ||||
|               path: "web/src/api/" | ||||
|           - task: Bash@3 | ||||
|             inputs: | ||||
|               targetType: 'inline' | ||||
|               script: | | ||||
|                 python ./scripts/az_do_set_branch.py | ||||
|           - task: Docker@2 | ||||
|             inputs: | ||||
|               containerRegistry: 'beryjuorg-harbor' | ||||
|               repository: 'authentik/static' | ||||
|               command: 'buildAndPush' | ||||
|               Dockerfile: 'web/Dockerfile' | ||||
|               tags: "gh-$(branchName)" | ||||
|               buildContext: 'web/' | ||||
|  | ||||
							
								
								
									
										4
									
								
								web/src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| apis/** | ||||
| models/** | ||||
| index.ts | ||||
| runtime.ts | ||||
							
								
								
									
										23
									
								
								web/src/api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| # OpenAPI Generator Ignore | ||||
| # Generated by openapi-generator https://github.com/openapitools/openapi-generator | ||||
|  | ||||
| # Use this file to prevent files from being overwritten by the generator. | ||||
| # The patterns follow closely to .gitignore or .dockerignore. | ||||
|  | ||||
| # As an example, the C# client generator defines ApiClient.cs. | ||||
| # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: | ||||
| #ApiClient.cs | ||||
|  | ||||
| # You can match any string of characters against a directory, file or extension with a single asterisk (*): | ||||
| #foo/*/qux | ||||
| # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux | ||||
|  | ||||
| # You can recursively match patterns against a directory, file or extension with a double asterisk (**): | ||||
| #foo/**/qux | ||||
| # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux | ||||
|  | ||||
| # You can also negate patterns with an exclamation (!). | ||||
| # For example, you can ignore all files in a docs folder with the file extension .md: | ||||
| #docs/*.md | ||||
| # Then explicitly reverse the ignore rule for a single file: | ||||
| #!docs/README.md | ||||
							
								
								
									
										167
									
								
								web/src/api/.openapi-generator/FILES
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								web/src/api/.openapi-generator/FILES
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,167 @@ | ||||
| apis/AdminApi.ts | ||||
| apis/CoreApi.ts | ||||
| apis/CryptoApi.ts | ||||
| apis/EventsApi.ts | ||||
| apis/FlowsApi.ts | ||||
| apis/OutpostsApi.ts | ||||
| apis/PoliciesApi.ts | ||||
| apis/PropertymappingsApi.ts | ||||
| apis/ProvidersApi.ts | ||||
| apis/RootApi.ts | ||||
| apis/SourcesApi.ts | ||||
| apis/StagesApi.ts | ||||
| apis/index.ts | ||||
| index.ts | ||||
| models/Application.ts | ||||
| models/AuthenticateWebAuthnStage.ts | ||||
| models/AuthenticatorStaticStage.ts | ||||
| models/AuthenticatorTOTPStage.ts | ||||
| models/AuthenticatorValidateStage.ts | ||||
| models/Cache.ts | ||||
| models/CaptchaStage.ts | ||||
| models/CertificateData.ts | ||||
| models/CertificateKeyPair.ts | ||||
| models/Challenge.ts | ||||
| models/Config.ts | ||||
| models/ConsentStage.ts | ||||
| models/Coordinate.ts | ||||
| models/DenyStage.ts | ||||
| models/DockerServiceConnection.ts | ||||
| models/DummyPolicy.ts | ||||
| models/DummyStage.ts | ||||
| models/EmailStage.ts | ||||
| models/ErrorDetail.ts | ||||
| models/Event.ts | ||||
| models/EventMatcherPolicy.ts | ||||
| models/EventTopPerUser.ts | ||||
| models/ExpressionPolicy.ts | ||||
| models/Flow.ts | ||||
| models/FlowDiagram.ts | ||||
| models/FlowStageBinding.ts | ||||
| models/Group.ts | ||||
| models/GroupMembershipPolicy.ts | ||||
| models/HaveIBeenPwendPolicy.ts | ||||
| models/IPReputation.ts | ||||
| models/IdentificationStage.ts | ||||
| models/InlineResponse200.ts | ||||
| models/InlineResponse2001.ts | ||||
| models/InlineResponse20010.ts | ||||
| models/InlineResponse20011.ts | ||||
| models/InlineResponse20012.ts | ||||
| models/InlineResponse20013.ts | ||||
| models/InlineResponse20014.ts | ||||
| models/InlineResponse20015.ts | ||||
| models/InlineResponse20016.ts | ||||
| models/InlineResponse20017.ts | ||||
| models/InlineResponse20018.ts | ||||
| models/InlineResponse20019.ts | ||||
| models/InlineResponse2002.ts | ||||
| models/InlineResponse20020.ts | ||||
| models/InlineResponse20021.ts | ||||
| models/InlineResponse20022.ts | ||||
| models/InlineResponse20023.ts | ||||
| models/InlineResponse20024.ts | ||||
| models/InlineResponse20025.ts | ||||
| models/InlineResponse20026.ts | ||||
| models/InlineResponse20027.ts | ||||
| models/InlineResponse20028.ts | ||||
| models/InlineResponse20029.ts | ||||
| models/InlineResponse2003.ts | ||||
| models/InlineResponse20030.ts | ||||
| models/InlineResponse20031.ts | ||||
| models/InlineResponse20032.ts | ||||
| models/InlineResponse20033.ts | ||||
| models/InlineResponse20034.ts | ||||
| models/InlineResponse20035.ts | ||||
| models/InlineResponse20036.ts | ||||
| models/InlineResponse20037.ts | ||||
| models/InlineResponse20038.ts | ||||
| models/InlineResponse20039.ts | ||||
| models/InlineResponse2004.ts | ||||
| models/InlineResponse20040.ts | ||||
| models/InlineResponse20041.ts | ||||
| models/InlineResponse20042.ts | ||||
| models/InlineResponse20043.ts | ||||
| models/InlineResponse20044.ts | ||||
| models/InlineResponse20045.ts | ||||
| models/InlineResponse20046.ts | ||||
| models/InlineResponse20047.ts | ||||
| models/InlineResponse20048.ts | ||||
| models/InlineResponse20049.ts | ||||
| models/InlineResponse2005.ts | ||||
| models/InlineResponse20050.ts | ||||
| models/InlineResponse20051.ts | ||||
| models/InlineResponse20052.ts | ||||
| models/InlineResponse20053.ts | ||||
| models/InlineResponse20054.ts | ||||
| models/InlineResponse20055.ts | ||||
| models/InlineResponse20056.ts | ||||
| models/InlineResponse20057.ts | ||||
| models/InlineResponse20058.ts | ||||
| models/InlineResponse20059.ts | ||||
| models/InlineResponse2006.ts | ||||
| models/InlineResponse20060.ts | ||||
| models/InlineResponse2007.ts | ||||
| models/InlineResponse2008.ts | ||||
| models/InlineResponse2009.ts | ||||
| models/InlineResponse200Pagination.ts | ||||
| models/Invitation.ts | ||||
| models/InvitationStage.ts | ||||
| models/KubernetesServiceConnection.ts | ||||
| models/LDAPPropertyMapping.ts | ||||
| models/LDAPSource.ts | ||||
| models/LDAPSourceSyncStatus.ts | ||||
| models/LoginMetrics.ts | ||||
| models/Notification.ts | ||||
| models/NotificationRule.ts | ||||
| models/NotificationRuleGroup.ts | ||||
| models/NotificationRuleGroupParent.ts | ||||
| models/NotificationRuleTransports.ts | ||||
| models/NotificationTransport.ts | ||||
| models/NotificationTransportTest.ts | ||||
| models/OAuth2Provider.ts | ||||
| models/OAuth2ProviderSetupURLs.ts | ||||
| models/OAuthSource.ts | ||||
| models/OpenIDConnectConfiguration.ts | ||||
| models/Outpost.ts | ||||
| models/OutpostHealth.ts | ||||
| models/PasswordExpiryPolicy.ts | ||||
| models/PasswordPolicy.ts | ||||
| models/PasswordStage.ts | ||||
| models/Policy.ts | ||||
| models/PolicyBinding.ts | ||||
| models/PolicyBindingPolicy.ts | ||||
| models/PolicyBindingUser.ts | ||||
| models/PolicyBindingUserAkGroups.ts | ||||
| models/PolicyBindingUserGroups.ts | ||||
| models/PolicyBindingUserSources.ts | ||||
| models/PolicyBindingUserUserPermissions.ts | ||||
| models/Prompt.ts | ||||
| models/PromptStage.ts | ||||
| models/PropertyMapping.ts | ||||
| models/Provider.ts | ||||
| models/ProxyOutpostConfig.ts | ||||
| models/ProxyProvider.ts | ||||
| models/ReputationPolicy.ts | ||||
| models/SAMLMetadata.ts | ||||
| models/SAMLPropertyMapping.ts | ||||
| models/SAMLProvider.ts | ||||
| models/SAMLSource.ts | ||||
| models/ScopeMapping.ts | ||||
| models/ServiceConnection.ts | ||||
| models/ServiceConnectionState.ts | ||||
| models/Source.ts | ||||
| models/Stage.ts | ||||
| models/Task.ts | ||||
| models/Token.ts | ||||
| models/TokenView.ts | ||||
| models/TypeCreate.ts | ||||
| models/User.ts | ||||
| models/UserDeleteStage.ts | ||||
| models/UserLoginStage.ts | ||||
| models/UserLogoutStage.ts | ||||
| models/UserReputation.ts | ||||
| models/UserWriteStage.ts | ||||
| models/Version.ts | ||||
| models/index.ts | ||||
| runtime.ts | ||||
							
								
								
									
										1
									
								
								web/src/api/.openapi-generator/VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/src/api/.openapi-generator/VERSION
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 5.1.0-SNAPSHOT | ||||
| @ -1,32 +0,0 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
| import { Provider } from "./Providers"; | ||||
|  | ||||
| export class Application { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     slug: string; | ||||
|     provider?: Provider; | ||||
|  | ||||
|     launch_url: string; | ||||
|     meta_launch_url: string; | ||||
|     meta_icon: string; | ||||
|     meta_description: string; | ||||
|     meta_publisher: string; | ||||
|     policies: string[]; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<Application> { | ||||
|         return DefaultClient.fetch<Application>(["core", "applications", slug]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Application>> { | ||||
|         return DefaultClient.fetch<AKResponse<Application>>(["core", "applications"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/applications/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
|  | ||||
| export class CertificateKeyPair { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     fingerprint: string; | ||||
|     cert_expiry: number; | ||||
|     cert_subject: string; | ||||
|     private_key_available: boolean; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<CertificateKeyPair> { | ||||
|         return DefaultClient.fetch<CertificateKeyPair>(["crypto", "certificatekeypairs", slug]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<CertificateKeyPair>> { | ||||
|         return DefaultClient.fetch<AKResponse<CertificateKeyPair>>(["crypto", "certificatekeypairs"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/crypto/certificates/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,10 +1,3 @@ | ||||
| import { gettext } from "django"; | ||||
| import { showMessage } from "../elements/messages/MessageContainer"; | ||||
| import { getCookie } from "../utils"; | ||||
| import { NotFoundError, RequestError } from "./Error"; | ||||
|  | ||||
| export const VERSION = "v2beta"; | ||||
|  | ||||
| export interface QueryArguments { | ||||
|     page?: number; | ||||
|     page_size?: number; | ||||
| @ -20,97 +13,20 @@ export interface BaseInheritanceModel { | ||||
|  | ||||
| } | ||||
|  | ||||
| export class Client { | ||||
|     makeUrl(url: string[], query?: QueryArguments): string { | ||||
|         let builtUrl = `/api/${VERSION}/${url.join("/")}/`; | ||||
|         if (query) { | ||||
|             const queryString = Object.keys(query) | ||||
|                 .filter((k) => query[k] !== null) | ||||
|                 // we default to a string in query[k] as we've filtered out the null above | ||||
|                 // this is just for type-hinting | ||||
|                 .map((k) => encodeURIComponent(k) + "=" + encodeURIComponent(query[k] || "")) | ||||
|                 .join("&"); | ||||
|             builtUrl += `?${queryString}`; | ||||
|         } | ||||
|         return builtUrl; | ||||
|     } | ||||
|  | ||||
|     fetch<T>(url: string[], query?: QueryArguments): Promise<T> { | ||||
|         const finalUrl = this.makeUrl(url, query); | ||||
|         return fetch(finalUrl) | ||||
|             .then((r) => { | ||||
|                 if (r.status > 300) { | ||||
|                     switch (r.status) { | ||||
|                     case 404: | ||||
|                         throw new NotFoundError(`URL ${finalUrl} not found`); | ||||
|                     default: | ||||
|                         throw new RequestError(r.statusText); | ||||
|                     } | ||||
|                 } | ||||
|                 return r; | ||||
|             }) | ||||
|             .catch((e) => { | ||||
|                 showMessage({ | ||||
|                     level_tag: "error", | ||||
|                     message: gettext(`Unexpected error while fetching: ${e.toString()}`), | ||||
|                 }); | ||||
|                 return e; | ||||
|             }) | ||||
|             .then((r) => r.json()) | ||||
|             .then((r) => <T>r); | ||||
|     } | ||||
|  | ||||
|     private writeRequest<T>(url: string[], body: T, method: string, query?: QueryArguments): Promise<T> { | ||||
|         const finalUrl = this.makeUrl(url, query); | ||||
|         const csrftoken = getCookie("authentik_csrf"); | ||||
|         const request = new Request(finalUrl, { | ||||
|             headers: { | ||||
|                 "Accept": "application/json", | ||||
|                 "Content-Type": "application/json", | ||||
|                 "X-CSRFToken": csrftoken, | ||||
|             }, | ||||
|         }); | ||||
|         return fetch(request, { | ||||
|             method: method, | ||||
|             mode: "same-origin", | ||||
|             body: JSON.stringify(body), | ||||
|         }) | ||||
|             .then((r) => { | ||||
|                 if (r.status > 300) { | ||||
|                     switch (r.status) { | ||||
|                     case 404: | ||||
|                         throw new NotFoundError(`URL ${finalUrl} not found`); | ||||
|                     default: | ||||
|                         throw new RequestError(r.statusText); | ||||
|                     } | ||||
|                 } | ||||
|                 return r; | ||||
|             }) | ||||
|             .then((r) => r.json()) | ||||
|             .then((r) => <T>r); | ||||
|     } | ||||
|  | ||||
|     update<T>(url: string[], body: T, query?: QueryArguments): Promise<T> { | ||||
|         return this.writeRequest(url, body, "PATCH", query); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export const DefaultClient = new Client(); | ||||
|  | ||||
| export interface PBPagination { | ||||
| export interface AKPagination { | ||||
|     next?: number; | ||||
|     previous?: number; | ||||
|  | ||||
|     count: number; | ||||
|     current: number; | ||||
|     total_pages: number; | ||||
|     totalPages: number; | ||||
|  | ||||
|     start_index: number; | ||||
|     end_index: number; | ||||
|     startIndex: number; | ||||
|     endIndex: number; | ||||
| } | ||||
|  | ||||
| export interface AKResponse<T> { | ||||
|     pagination: PBPagination; | ||||
|     pagination: AKPagination; | ||||
|  | ||||
|     results: Array<T>; | ||||
| } | ||||
|  | ||||
| @ -1,42 +1,39 @@ | ||||
| import { DefaultClient } from "./Client"; | ||||
| import * as Sentry from "@sentry/browser"; | ||||
| import { Integrations } from "@sentry/tracing"; | ||||
| import { VERSION } from "../constants"; | ||||
| import { SentryIgnoredError } from "../common/errors"; | ||||
| import { Configuration } from "./runtime"; | ||||
| import { RootApi } from "./apis"; | ||||
| import { Config } from "."; | ||||
| import { getCookie } from "../utils"; | ||||
|  | ||||
| export class Config { | ||||
|     branding_logo: string; | ||||
|     branding_title: string; | ||||
|  | ||||
|     error_reporting_enabled: boolean; | ||||
|     error_reporting_environment: string; | ||||
|     error_reporting_send_pii: boolean; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
| export const DEFAULT_CONFIG = new Configuration({ | ||||
|     basePath: "/api/v2beta", | ||||
|     headers: { | ||||
|         "X-CSRFToken": getCookie("authentik_csrf"), | ||||
|     } | ||||
| }); | ||||
|  | ||||
|     static get(): Promise<Config> { | ||||
|         return DefaultClient.fetch<Config>(["root", "config"]).then((config) => { | ||||
|             if (config.error_reporting_enabled) { | ||||
|                 Sentry.init({ | ||||
|                     dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", | ||||
|                     release: `authentik@${VERSION}`, | ||||
|                     integrations: [ | ||||
|                         new Integrations.BrowserTracing(), | ||||
|                     ], | ||||
|                     tracesSampleRate: 0.6, | ||||
|                     environment: config.error_reporting_environment, | ||||
|                     beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { | ||||
|                         if (hint.originalException instanceof SentryIgnoredError) { | ||||
|                             return null; | ||||
|                         } | ||||
|                         return event; | ||||
|                     }, | ||||
|                 }); | ||||
|                 console.debug("authentik/config: Sentry enabled."); | ||||
|             } | ||||
|             return config; | ||||
|         }); | ||||
|     } | ||||
| export function configureSentry(): Promise<Config> { | ||||
|     return new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => { | ||||
|         if (config.errorReportingEnabled) { | ||||
|             Sentry.init({ | ||||
|                 dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", | ||||
|                 release: `authentik@${VERSION}`, | ||||
|                 integrations: [ | ||||
|                     new Integrations.BrowserTracing(), | ||||
|                 ], | ||||
|                 tracesSampleRate: 0.6, | ||||
|                 environment: config.errorReportingEnvironment, | ||||
|                 beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { | ||||
|                     if (hint.originalException instanceof SentryIgnoredError) { | ||||
|                         return null; | ||||
|                     } | ||||
|                     return event; | ||||
|                 }, | ||||
|             }); | ||||
|             console.debug("authentik/config: Sentry enabled."); | ||||
|         } | ||||
|         return config; | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @ -1,30 +0,0 @@ | ||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||
| import { Event } from "./Events"; | ||||
|  | ||||
| export class Notification { | ||||
|     pk: string; | ||||
|     severity: string; | ||||
|     body: string; | ||||
|     created: string; | ||||
|     event?: Event; | ||||
|     seen: boolean; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Notification> { | ||||
|         return DefaultClient.fetch<Notification>(["events", "notifications", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Notification>> { | ||||
|         return DefaultClient.fetch<AKResponse<Notification>>(["events", "notifications"], filter); | ||||
|     } | ||||
|  | ||||
|     static markSeen(pk: string): Promise<{seen: boolean}> { | ||||
|         return DefaultClient.update(["events", "notifications", pk], { | ||||
|             "seen": true | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||
| import { Group } from "./Groups"; | ||||
|  | ||||
| export class Rule { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     transports: string[]; | ||||
|     severity: string; | ||||
|     group?: Group; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Rule> { | ||||
|         return DefaultClient.fetch<Rule>(["events", "rules", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Rule>> { | ||||
|         return DefaultClient.fetch<AKResponse<Rule>>(["events", "rules"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/events/rules/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||
|  | ||||
| export class Transport { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     mode: string; | ||||
|     mode_verbose: string; | ||||
|     webhook_url: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Transport> { | ||||
|         return DefaultClient.fetch<Transport>(["events", "transports", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Transport>> { | ||||
|         return DefaultClient.fetch<AKResponse<Transport>>(["events", "transports"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/events/transports/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
| import { Event } from "./models"; | ||||
|  | ||||
| export interface EventUser { | ||||
|     pk: number; | ||||
| @ -11,37 +11,7 @@ export interface EventContext { | ||||
|     [key: string]: EventContext | string | number | string[]; | ||||
| } | ||||
|  | ||||
| export class Event { | ||||
|     pk: string; | ||||
| export interface EventWithContext extends Event { | ||||
|     user: EventUser; | ||||
|     action: string; | ||||
|     app: string; | ||||
|     context: EventContext; | ||||
|     client_ip: string; | ||||
|     created: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Event> { | ||||
|         return DefaultClient.fetch<Event>(["events", "events", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Event>> { | ||||
|         return DefaultClient.fetch<AKResponse<Event>>(["events", "events"], filter); | ||||
|     } | ||||
|  | ||||
|     // events/events/top_per_user/?filter_action=authorize_application | ||||
|     static topForUser(action: string): Promise<TopNEvent[]> { | ||||
|         return DefaultClient.fetch<TopNEvent[]>(["events", "events", "top_per_user"], { | ||||
|             "filter_action": action, | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export interface TopNEvent { | ||||
|     application: { [key: string]: string}; | ||||
|     counted_events: number; | ||||
|     unique_users: number; | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,4 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments, BaseInheritanceModel } from "./Client"; | ||||
| import { TypeCreate } from "./Providers"; | ||||
|  | ||||
| export enum ChallengeTypes { | ||||
|     native = "native", | ||||
|     response = "response", | ||||
|     shell = "shell", | ||||
|     redirect = "redirect", | ||||
| } | ||||
| import { ChallengeTypeEnum } from "./models"; | ||||
|  | ||||
| export interface Error { | ||||
|     code: string; | ||||
| @ -18,11 +10,12 @@ export interface ErrorDict { | ||||
| } | ||||
|  | ||||
| export interface Challenge { | ||||
|     type: ChallengeTypes; | ||||
|     type: ChallengeTypeEnum; | ||||
|     component?: string; | ||||
|     title?: string; | ||||
|     response_errors?: ErrorDict; | ||||
| } | ||||
|  | ||||
| export interface WithUserInfoChallenge extends Challenge { | ||||
|     pending_user: string; | ||||
|     pending_user_avatar: string; | ||||
| @ -31,6 +24,7 @@ export interface WithUserInfoChallenge extends Challenge { | ||||
| export interface ShellChallenge extends Challenge { | ||||
|     body: string; | ||||
| } | ||||
|  | ||||
| export interface RedirectChallenge extends Challenge { | ||||
|     to: string; | ||||
| } | ||||
| @ -44,104 +38,3 @@ export enum FlowDesignation { | ||||
|     Recovery = "recovery", | ||||
|     StageConfiguration = "stage_configuration", | ||||
| } | ||||
|  | ||||
| export class Flow { | ||||
|     pk: string; | ||||
|     policybindingmodel_ptr_id: string; | ||||
|     name: string; | ||||
|     slug: string; | ||||
|     title: string; | ||||
|     designation: FlowDesignation; | ||||
|     background: string; | ||||
|     stages: string[]; | ||||
|     policies: string[]; | ||||
|     cache_count: number; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<Flow> { | ||||
|         return DefaultClient.fetch<Flow>(["flows", "instances", slug]); | ||||
|     } | ||||
|  | ||||
|     static diagram(slug: string): Promise<{ diagram: string }> { | ||||
|         return DefaultClient.fetch<{ diagram: string }>(["flows", "instances", slug, "diagram"]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Flow>> { | ||||
|         return DefaultClient.fetch<AKResponse<Flow>>(["flows", "instances"], filter); | ||||
|     } | ||||
|  | ||||
|     static cached(): Promise<number> { | ||||
|         return DefaultClient.fetch<{ count: number }>(["flows", "instances", "cached"]).then(r => { | ||||
|             return r.count; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     static executor(slug: string): Promise<Challenge> { | ||||
|         return DefaultClient.fetch(["flows", "executor", slug]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/flows/${rest}`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class Stage implements BaseInheritanceModel { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     object_type: string; | ||||
|     verbose_name: string; | ||||
|     verbose_name_plural: string; | ||||
|     flow_set: Flow[]; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<Stage> { | ||||
|         return DefaultClient.fetch<Stage>(["stages", "all", slug]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Stage>> { | ||||
|         return DefaultClient.fetch<AKResponse<Stage>>(["stages", "all"], filter); | ||||
|     } | ||||
|  | ||||
|     static getTypes(): Promise<TypeCreate[]> { | ||||
|         return DefaultClient.fetch<TypeCreate[]>(["stages", "all", "types"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/stages/${rest}`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class FlowStageBinding { | ||||
|  | ||||
|     pk: string; | ||||
|     policybindingmodel_ptr_id: string; | ||||
|     target: string; | ||||
|     stage: string; | ||||
|     stage_obj: Stage; | ||||
|     evaluate_on_plan: boolean; | ||||
|     re_evaluate_policies: boolean; | ||||
|     order: number; | ||||
|     policies: string[]; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<FlowStageBinding> { | ||||
|         return DefaultClient.fetch<FlowStageBinding>(["flows", "bindings", slug]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<FlowStageBinding>> { | ||||
|         return DefaultClient.fetch<AKResponse<FlowStageBinding>>(["flows", "bindings"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/stages/bindings/${rest}`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,28 +0,0 @@ | ||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||
| import { EventContext } from "./Events"; | ||||
|  | ||||
| export class Group { | ||||
|  | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     is_superuser: boolean; | ||||
|     attributes: EventContext; | ||||
|     parent?: Group; | ||||
|     users: number[]; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Group> { | ||||
|         return DefaultClient.fetch<Group>(["core", "groups", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Group>> { | ||||
|         return DefaultClient.fetch<AKResponse<Group>>(["core", "groups"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/groups/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||
| import { EventContext } from "./Events"; | ||||
| import { User } from "./Users"; | ||||
|  | ||||
| export class Invitation { | ||||
|  | ||||
|     pk: string; | ||||
|     expires: number; | ||||
|     fixed_date: EventContext; | ||||
|     created_by: User; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Invitation> { | ||||
|         return DefaultClient.fetch<Invitation>(["stages", "invitation", "invitations", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Invitation>> { | ||||
|         return DefaultClient.fetch<AKResponse<Invitation>>(["stages", "invitation", "invitations"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/stages/invitations/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,79 +0,0 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
| import { Provider, TypeCreate } from "./Providers"; | ||||
|  | ||||
| export interface OutpostHealth { | ||||
|     last_seen: number; | ||||
|     version: string; | ||||
|     version_should: string; | ||||
|     version_outdated: boolean; | ||||
| } | ||||
|  | ||||
| export class Outpost { | ||||
|  | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     providers: number[]; | ||||
|     providers_obj: Provider[]; | ||||
|     service_connection?: string; | ||||
|     _config: QueryArguments; | ||||
|     token_identifier: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Outpost> { | ||||
|         return DefaultClient.fetch<Outpost>(["outposts", "outposts", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Outpost>> { | ||||
|         return DefaultClient.fetch<AKResponse<Outpost>>(["outposts", "outposts"], filter); | ||||
|     } | ||||
|  | ||||
|     static health(pk: string): Promise<OutpostHealth[]> { | ||||
|         return DefaultClient.fetch<OutpostHealth[]>(["outposts", "outposts", pk, "health"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/outposts/${rest}`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export interface OutpostServiceConnectionState { | ||||
|     version: string; | ||||
|     healthy: boolean; | ||||
| } | ||||
|  | ||||
| export class OutpostServiceConnection { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     local: boolean; | ||||
|     object_type: string; | ||||
|     verbose_name: string; | ||||
|     verbose_name_plural: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<OutpostServiceConnection> { | ||||
|         return DefaultClient.fetch<OutpostServiceConnection>(["outposts", "service_connections", "all", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<OutpostServiceConnection>> { | ||||
|         return DefaultClient.fetch<AKResponse<OutpostServiceConnection>>(["outposts", "service_connections", "all"], filter); | ||||
|     } | ||||
|  | ||||
|     static state(pk: string): Promise<OutpostServiceConnectionState> { | ||||
|         return DefaultClient.fetch<OutpostServiceConnectionState>(["outposts", "service_connections", "all", pk, "state"]); | ||||
|     } | ||||
|  | ||||
|     static getTypes(): Promise<TypeCreate[]> { | ||||
|         return DefaultClient.fetch<TypeCreate[]>(["outposts", "service_connections", "all", "types"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/outpost_service_connections/${rest}`; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,38 +0,0 @@ | ||||
| import { DefaultClient, BaseInheritanceModel, AKResponse, QueryArguments } from "./Client"; | ||||
| import { TypeCreate } from "./Providers"; | ||||
|  | ||||
| export class Policy implements BaseInheritanceModel { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     execution_logging: boolean; | ||||
|     object_type: string; | ||||
|     verbose_name: string; | ||||
|     verbose_name_plural: string; | ||||
|     bound_to: number; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Policy> { | ||||
|         return DefaultClient.fetch<Policy>(["policies", "all", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Policy>> { | ||||
|         return DefaultClient.fetch<AKResponse<Policy>>(["policies", "all"], filter); | ||||
|     } | ||||
|  | ||||
|     static cached(): Promise<number> { | ||||
|         return DefaultClient.fetch<{ count: number }>(["policies", "all", "cached"]).then(r => { | ||||
|             return r.count; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     static getTypes(): Promise<TypeCreate[]> { | ||||
|         return DefaultClient.fetch<TypeCreate[]>(["policies", "all", "types"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/policies/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
| import { Group } from "./Groups"; | ||||
| import { Policy } from "./Policies"; | ||||
| import { User } from "./Users"; | ||||
|  | ||||
| export class PolicyBinding { | ||||
|     pk: string; | ||||
|     policy?: Policy; | ||||
|     group?: Group; | ||||
|     user?: User; | ||||
|     target: string; | ||||
|     enabled: boolean; | ||||
|     order: number; | ||||
|     timeout: number; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<PolicyBinding> { | ||||
|         return DefaultClient.fetch<PolicyBinding>(["policies", "bindings", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<PolicyBinding>> { | ||||
|         return DefaultClient.fetch<AKResponse<PolicyBinding>>(["policies", "bindings"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/policies/bindings/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; | ||||
| import { Stage } from "./Flows"; | ||||
|  | ||||
| export class Prompt { | ||||
|  | ||||
|     pk: string; | ||||
|     field_key: string; | ||||
|     label: string; | ||||
|     type: string; | ||||
|     required: boolean; | ||||
|     placeholder: string; | ||||
|     order: number; | ||||
|     promptstage_set: Stage[]; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<Prompt> { | ||||
|         return DefaultClient.fetch<Prompt>(["stages", "prompt", "prompts", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Prompt>> { | ||||
|         return DefaultClient.fetch<AKResponse<Prompt>>(["stages", "prompt", "prompts"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/stages_prompts/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
| import { TypeCreate } from "./Providers"; | ||||
|  | ||||
| export class PropertyMapping { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     expression: string; | ||||
|  | ||||
|     verbose_name: string; | ||||
|     verbose_name_plural: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<PropertyMapping> { | ||||
|         return DefaultClient.fetch<PropertyMapping>(["propertymappings", "all", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<PropertyMapping>> { | ||||
|         return DefaultClient.fetch<AKResponse<PropertyMapping>>(["propertymappings", "all"], filter); | ||||
|     } | ||||
|  | ||||
|     static getTypes(): Promise<TypeCreate[]> { | ||||
|         return DefaultClient.fetch<TypeCreate[]>(["propertymappings", "all", "types"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/property-mappings/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,40 +0,0 @@ | ||||
| import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
|  | ||||
| export interface TypeCreate { | ||||
|     name: string; | ||||
|     description: string; | ||||
|     link: string; | ||||
| } | ||||
|  | ||||
| export class Provider implements BaseInheritanceModel { | ||||
|     pk: number; | ||||
|     name: string; | ||||
|     authorization_flow: string; | ||||
|     object_type: string; | ||||
|  | ||||
|     assigned_application_slug?: string; | ||||
|     assigned_application_name?: string; | ||||
|  | ||||
|     verbose_name: string; | ||||
|     verbose_name_plural: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(id: number): Promise<Provider> { | ||||
|         return DefaultClient.fetch<Provider>(["providers", "all", id.toString()]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Provider>> { | ||||
|         return DefaultClient.fetch<AKResponse<Provider>>(["providers", "all"], filter); | ||||
|     } | ||||
|  | ||||
|     static getTypes(): Promise<TypeCreate[]> { | ||||
|         return DefaultClient.fetch<TypeCreate[]>(["providers", "all", "types"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/providers/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,34 +0,0 @@ | ||||
| import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
| import { TypeCreate } from "./Providers"; | ||||
|  | ||||
| export class Source implements BaseInheritanceModel { | ||||
|     pk: string; | ||||
|     name: string; | ||||
|     slug: string; | ||||
|     enabled: boolean; | ||||
|     authentication_flow: string; | ||||
|     enrollment_flow: string; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|     object_type: string; | ||||
|     verbose_name: string; | ||||
|     verbose_name_plural: string; | ||||
|  | ||||
|     static get(slug: string): Promise<Source> { | ||||
|         return DefaultClient.fetch<Source>(["sources", "all", slug]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Source>> { | ||||
|         return DefaultClient.fetch<AKResponse<Source>>(["sources", "all"], filter); | ||||
|     } | ||||
|  | ||||
|     static getTypes(): Promise<TypeCreate[]> { | ||||
|         return DefaultClient.fetch<TypeCreate[]>(["sources", "all", "types"]); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/sources/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| import { DefaultClient, QueryArguments } from "./Client"; | ||||
|  | ||||
| export enum TaskStatus { | ||||
|     SUCCESSFUL = 1, | ||||
|     WARNING = 2, | ||||
|     ERROR = 4, | ||||
| } | ||||
|  | ||||
| export class SystemTask { | ||||
|  | ||||
|     task_name: string; | ||||
|     task_description: string; | ||||
|     task_finish_timestamp: number; | ||||
|     status: TaskStatus; | ||||
|     messages: string[]; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(task_name: string): Promise<SystemTask> { | ||||
|         return DefaultClient.fetch<SystemTask>(["admin", "system_tasks", task_name]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<SystemTask[]> { | ||||
|         return DefaultClient.fetch<SystemTask[]>(["admin", "system_tasks"], filter); | ||||
|     } | ||||
|  | ||||
|     static retry(task_name: string): string { | ||||
|         return DefaultClient.makeUrl(["admin", "system_tasks", task_name, "retry"]); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,47 +0,0 @@ | ||||
| import { AKResponse, DefaultClient, QueryArguments } from "./Client"; | ||||
| import { User } from "./Users"; | ||||
|  | ||||
| export enum TokenIntent { | ||||
|     INTENT_VERIFICATION = "verification", | ||||
|     INTENT_API = "api", | ||||
|     INTENT_RECOVERY = "recovery", | ||||
| } | ||||
|  | ||||
| export class Token { | ||||
|  | ||||
|     pk: string; | ||||
|     identifier: string; | ||||
|     intent: TokenIntent; | ||||
|     user: User; | ||||
|     description: string; | ||||
|  | ||||
|     expires: number; | ||||
|     expiring: boolean; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<User> { | ||||
|         return DefaultClient.fetch<User>(["core", "tokens", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Token>> { | ||||
|         return DefaultClient.fetch<AKResponse<Token>>(["core", "tokens"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/tokens/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static userUrl(rest: string): string { | ||||
|         return `/-/user/tokens/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static getKey(identifier: string): Promise<string> { | ||||
|         return DefaultClient.fetch<{ key: string }>(["core", "tokens", identifier, "view_key"]).then( | ||||
|             (r) => r.key | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,45 +1,11 @@ | ||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | ||||
| import { CoreApi } from "./apis"; | ||||
| import { DEFAULT_CONFIG } from "./Config"; | ||||
| import { User } from "./models"; | ||||
|  | ||||
| let _globalMePromise: Promise<User>; | ||||
|  | ||||
| export class User { | ||||
|     pk: number; | ||||
|     username: string; | ||||
|     name: string; | ||||
|     is_superuser: boolean; | ||||
|     email: boolean; | ||||
|     avatar: string; | ||||
|     is_active: boolean; | ||||
|     last_login: number; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(pk: string): Promise<User> { | ||||
|         return DefaultClient.fetch<User>(["core", "users", pk]); | ||||
|     } | ||||
|  | ||||
|     static list(filter?: QueryArguments): Promise<AKResponse<User>> { | ||||
|         return DefaultClient.fetch<AKResponse<User>>(["core", "users"], filter); | ||||
|     } | ||||
|  | ||||
|     static adminUrl(rest: string): string { | ||||
|         return `/administration/users/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static me(): Promise<User> { | ||||
|         if (!_globalMePromise) { | ||||
|             _globalMePromise = DefaultClient.fetch<User>(["core", "users", "me"]); | ||||
|         } | ||||
|         return _globalMePromise; | ||||
|     } | ||||
|  | ||||
|     static count(): Promise<number> { | ||||
|         return DefaultClient.fetch<AKResponse<User>>(["core", "users"], { | ||||
|             "page_size": 1 | ||||
|         }).then(r => { | ||||
|             return r.pagination.count; | ||||
|         }); | ||||
| export function me(): Promise<User> { | ||||
|     if (!_globalMePromise) { | ||||
|         _globalMePromise = new CoreApi(DEFAULT_CONFIG).coreUsersMe({}); | ||||
|     } | ||||
|     return _globalMePromise; | ||||
| } | ||||
|  | ||||
| @ -1,17 +0,0 @@ | ||||
| import { DefaultClient } from "./Client"; | ||||
|  | ||||
| export class Version { | ||||
|  | ||||
|     version_current: string; | ||||
|     version_latest: string; | ||||
|     outdated: boolean; | ||||
|  | ||||
|     constructor() { | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(): Promise<Version> { | ||||
|         return DefaultClient.fetch<Version>(["admin", "version"]); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										97
									
								
								web/src/api/legacy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								web/src/api/legacy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| export class AdminURLManager { | ||||
|  | ||||
|     static applications(rest: string): string { | ||||
|         return `/administration/applications/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static cryptoCertificates(rest: string): string { | ||||
|         return `/administration/crypto/certificates/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static policies(rest: string): string { | ||||
|         return `/administration/policies/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static policyBindings(rest: string): string { | ||||
|         return `/administration/policies/bindings/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static providers(rest: string): string { | ||||
|         return `/administration/providers/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static propertyMappings(rest: string): string { | ||||
|         return `/administration/property-mappings/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static outposts(rest: string): string { | ||||
|         return `/administration/outposts/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static outpostServiceConnections(rest: string): string { | ||||
|         return `/administration/outpost_service_connections/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static flows(rest: string): string { | ||||
|         return `/administration/flows/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static stages(rest: string): string { | ||||
|         return `/administration/stages/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static stagePrompts(rest: string): string { | ||||
|         return `/administration/stages_prompts/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static stageInvitations(rest: string): string { | ||||
|         return `/administration/stages/invitations/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static stageBindings(rest: string): string { | ||||
|         return `/administration/stages/bindings/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static sources(rest: string): string { | ||||
|         return `/administration/sources/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static tokens(rest: string): string { | ||||
|         return `/administration/tokens/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static eventRules(rest: string): string { | ||||
|         return `/administration/events/rules/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static eventTransports(rest: string): string { | ||||
|         return `/administration/events/transports/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static users(rest: string): string { | ||||
|         return `/administration/users/${rest}`; | ||||
|     } | ||||
|  | ||||
|     static groups(rest: string): string { | ||||
|         return `/administration/groups/${rest}`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class UserURLManager { | ||||
|  | ||||
|     static tokens(rest: string): string { | ||||
|         return `/-/user/tokens/${rest}`; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| export class AppURLManager { | ||||
|  | ||||
|     static sourceSAML(slug: string, rest: string): string { | ||||
|         return `/source/saml/${slug}/${rest}`; | ||||
|     } | ||||
|     static providerSAML(rest: string): string { | ||||
|         return `/application/saml/${rest}`; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| import { DefaultClient } from "../Client"; | ||||
| import { Provider } from "../Providers"; | ||||
|  | ||||
| export interface OAuth2SetupURLs { | ||||
|  | ||||
|     issuer?: string; | ||||
|     authorize: string; | ||||
|     token: string; | ||||
|     user_info: string; | ||||
|     provider_info?: string; | ||||
|     logout?: string; | ||||
|  | ||||
| } | ||||
|  | ||||
| export class OAuth2Provider extends Provider { | ||||
|     client_type: string | ||||
|     client_id: string; | ||||
|     client_secret: string; | ||||
|     token_validity: string; | ||||
|     include_claims_in_id_token: boolean; | ||||
|     jwt_alg: string; | ||||
|     rsa_key: string; | ||||
|     redirect_uris: string; | ||||
|     sub_mode: string; | ||||
|     issuer_mode: string; | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(id: number): Promise<OAuth2Provider> { | ||||
|         return DefaultClient.fetch<OAuth2Provider>(["providers", "oauth2", id.toString()]); | ||||
|     } | ||||
|  | ||||
|     static getLaunchURls(id: number): Promise<OAuth2SetupURLs> { | ||||
|         return DefaultClient.fetch(["providers", "oauth2", id.toString(), "setup_urls"]); | ||||
|     } | ||||
|  | ||||
|     static appUrl(rest: string): string { | ||||
|         return `/application/oauth2/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| import { DefaultClient } from "../Client"; | ||||
| import { Provider } from "../Providers"; | ||||
|  | ||||
| export class ProxyProvider extends Provider { | ||||
|     internal_host: string; | ||||
|     external_host: string; | ||||
|     internal_host_ssl_validation: boolean | ||||
|     certificate?: string; | ||||
|     skip_path_regex: string; | ||||
|     basic_auth_enabled: boolean; | ||||
|     basic_auth_password_attribute: string; | ||||
|     basic_auth_user_attribute: string; | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(id: number): Promise<ProxyProvider> { | ||||
|         return DefaultClient.fetch<ProxyProvider>(["providers", "proxy", id.toString()]); | ||||
|     } | ||||
|  | ||||
|     static getMetadata(id: number): Promise<{ metadata: string }> { | ||||
|         return DefaultClient.fetch(["providers", "proxy", id.toString(), "metadata"]); | ||||
|     } | ||||
|  | ||||
|     static appUrl(rest: string): string { | ||||
|         return `/application/proxy/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| import { DefaultClient } from "../Client"; | ||||
| import { Provider } from "../Providers"; | ||||
|  | ||||
| export class SAMLProvider extends Provider { | ||||
|     acs_url: string; | ||||
|     audience: string; | ||||
|     issuer: string; | ||||
|     assertion_valid_not_before: string; | ||||
|     assertion_valid_not_on_or_after: string; | ||||
|     session_valid_not_on_or_after: string; | ||||
|     name_id_mapping?: string; | ||||
|     digest_algorithm: string; | ||||
|     signature_algorithm: string; | ||||
|     signing_kp?: string; | ||||
|     verification_kp?: string; | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(id: number): Promise<SAMLProvider> { | ||||
|         return DefaultClient.fetch<SAMLProvider>(["providers", "saml", id.toString()]); | ||||
|     } | ||||
|  | ||||
|     static getMetadata(id: number): Promise<{ metadata: string }> { | ||||
|         return DefaultClient.fetch(["providers", "saml", id.toString(), "metadata"]); | ||||
|     } | ||||
|  | ||||
|     static appUrl(rest: string): string { | ||||
|         return `/application/saml/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -1,35 +0,0 @@ | ||||
| import { DefaultClient } from "../Client"; | ||||
| import { Source } from "../Sources"; | ||||
|  | ||||
| export class LDAPSource extends Source { | ||||
|     server_uri: string; | ||||
|     bind_cn: string; | ||||
|     start_tls: boolean | ||||
|     base_dn: string; | ||||
|     additional_user_dn: string; | ||||
|     additional_group_dn: string; | ||||
|     user_object_filter: string; | ||||
|     group_object_filter: string; | ||||
|     group_membership_field: string; | ||||
|     object_uniqueness_field: string; | ||||
|     sync_users: boolean; | ||||
|     sync_users_password: boolean; | ||||
|     sync_groups: boolean; | ||||
|     sync_parent_group?: string; | ||||
|     property_mappings: string[]; | ||||
|     property_mappings_group: string[]; | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<LDAPSource> { | ||||
|         return DefaultClient.fetch<LDAPSource>(["sources", "ldap", slug]); | ||||
|     } | ||||
|  | ||||
|     static syncStatus(slug: string): Promise<{ last_sync?: number }> { | ||||
|         return DefaultClient.fetch(["sources", "ldap", slug, "sync_status"]); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| import { DefaultClient } from "../Client"; | ||||
| import { Source } from "../Sources"; | ||||
|  | ||||
| export class OAuthSource extends Source { | ||||
|     provider_type: string; | ||||
|     request_token_url: string; | ||||
|     authorization_url: string; | ||||
|     access_token_url: string; | ||||
|     profile_url: string; | ||||
|     consumer_key: string; | ||||
|     callback_url: string; | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<OAuthSource> { | ||||
|         return DefaultClient.fetch<OAuthSource>(["sources", "oauth", slug]); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,32 +0,0 @@ | ||||
| import { DefaultClient } from "../Client"; | ||||
| import { Source } from "../Sources"; | ||||
|  | ||||
| export class SAMLSource extends Source { | ||||
|     issuer: string; | ||||
|     sso_url: string; | ||||
|     slo_url: string; | ||||
|     allow_idp_initiated: boolean; | ||||
|     name_id_policy: string; | ||||
|     binding_type: string | ||||
|     signing_kp?: string; | ||||
|     digest_algorithm: string; | ||||
|     signature_algorithm: string; | ||||
|     temporary_user_delete_after: string; | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         throw Error(); | ||||
|     } | ||||
|  | ||||
|     static get(slug: string): Promise<SAMLSource> { | ||||
|         return DefaultClient.fetch<SAMLSource>(["sources", "saml", slug]); | ||||
|     } | ||||
|  | ||||
|     static getMetadata(slug: string): Promise<{ metadata: string }> { | ||||
|         return DefaultClient.fetch(["sources", "saml", slug, "metadata"]); | ||||
|     } | ||||
|  | ||||
|     static appUrl(slug: string, rest: string): string { | ||||
|         return `/source/saml/${slug}/${rest}`; | ||||
|     } | ||||
| } | ||||
| @ -157,7 +157,7 @@ ak-message { | ||||
|         color: var(--ak-dark-foreground) !important; | ||||
|     } | ||||
|     /* tabs, vertical */ | ||||
|     .pf-c-tabs__link { | ||||
|     .pf-c-tabs.pf-m-vertical .pf-c-tabs__link { | ||||
|         background-color: var(--ak-dark-background-light); | ||||
|     } | ||||
|     /* table, on mobile */ | ||||
|  | ||||
| @ -1,119 +0,0 @@ | ||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import Chart from "chart.js"; | ||||
| import { DefaultClient } from "../api/Client"; | ||||
|  | ||||
| interface TickValue { | ||||
|     value: number; | ||||
|     major: boolean; | ||||
| } | ||||
|  | ||||
| export interface LoginMetrics { | ||||
|     logins_failed_per_1h: { x: number, y: number }[]; | ||||
|     logins_per_1h: { x: number, y: number }[]; | ||||
| } | ||||
|  | ||||
| @customElement("ak-admin-logins-chart") | ||||
| export class AdminLoginsChart extends LitElement { | ||||
|     @property({type: Array}) | ||||
|     url: string[] = []; | ||||
|  | ||||
|     chart?: Chart; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return [css` | ||||
|             :host { | ||||
|                 position: relative; | ||||
|                 height: 100%; | ||||
|                 width: 100%; | ||||
|                 display: block; | ||||
|                 min-height: 25rem; | ||||
|             } | ||||
|             canvas { | ||||
|                 width: 100px; | ||||
|                 height: 100px; | ||||
|             } | ||||
|         `]; | ||||
|     } | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         window.addEventListener("resize", () => { | ||||
|             if (this.chart) { | ||||
|                 this.chart.resize(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         DefaultClient.fetch<LoginMetrics>(this.url) | ||||
|             .then((r) => { | ||||
|                 const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas"); | ||||
|                 if (!canvas) { | ||||
|                     console.warn("Failed to get canvas element"); | ||||
|                     return false; | ||||
|                 } | ||||
|                 const ctx = canvas.getContext("2d"); | ||||
|                 if (!ctx) { | ||||
|                     console.warn("failed to get 2d context"); | ||||
|                     return false; | ||||
|                 } | ||||
|                 this.chart = new Chart(ctx, { | ||||
|                     type: "bar", | ||||
|                     data: { | ||||
|                         datasets: [ | ||||
|                             { | ||||
|                                 label: "Failed Logins", | ||||
|                                 backgroundColor: "rgba(201, 25, 11, .5)", | ||||
|                                 spanGaps: true, | ||||
|                                 data: r.logins_failed_per_1h, | ||||
|                             }, | ||||
|                             { | ||||
|                                 label: "Successful Logins", | ||||
|                                 backgroundColor: "rgba(189, 229, 184, .5)", | ||||
|                                 spanGaps: true, | ||||
|                                 data: r.logins_per_1h, | ||||
|                             }, | ||||
|                         ], | ||||
|                     }, | ||||
|                     options: { | ||||
|                         maintainAspectRatio: false, | ||||
|                         spanGaps: true, | ||||
|                         scales: { | ||||
|                             xAxes: [ | ||||
|                                 { | ||||
|                                     stacked: true, | ||||
|                                     gridLines: { | ||||
|                                         color: "rgba(0, 0, 0, 0)", | ||||
|                                     }, | ||||
|                                     type: "time", | ||||
|                                     offset: true, | ||||
|                                     ticks: { | ||||
|                                         callback: function (value, index: number, values) { | ||||
|                                             const valueStamp = <TickValue>(<unknown>values[index]); | ||||
|                                             const delta = Date.now() - valueStamp.value; | ||||
|                                             const ago = Math.round(delta / 1000 / 3600); | ||||
|                                             return `${ago} Hours ago`; | ||||
|                                         }, | ||||
|                                         autoSkip: true, | ||||
|                                         maxTicksLimit: 8, | ||||
|                                     }, | ||||
|                                 }, | ||||
|                             ], | ||||
|                             yAxes: [ | ||||
|                                 { | ||||
|                                     stacked: true, | ||||
|                                     gridLines: { | ||||
|                                         color: "rgba(0, 0, 0, 0)", | ||||
|                                     }, | ||||
|                                 }, | ||||
|                             ], | ||||
|                         }, | ||||
|                     }, | ||||
|                 }); | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         return html`<canvas></canvas>`; | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,3 @@ | ||||
| import { getCookie } from "../../utils"; | ||||
| import { customElement, property } from "lit-element"; | ||||
| import { ERROR_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||
| import { SpinnerButton } from "./SpinnerButton"; | ||||
| @ -12,43 +11,33 @@ export class ActionButton extends SpinnerButton { | ||||
|     @property() | ||||
|     method = "POST"; | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||
|     apiRequest: () => Promise<any> = () => { throw new Error(); }; | ||||
|  | ||||
|     callAction(): void { | ||||
|         if (this.isRunning === true) { | ||||
|             return; | ||||
|         } | ||||
|         this.setLoading(); | ||||
|         const csrftoken = getCookie("authentik_csrf"); | ||||
|         const request = new Request(this.url, { | ||||
|             headers: { "X-CSRFToken": csrftoken }, | ||||
|         }); | ||||
|         fetch(request, { | ||||
|             method: this.method, | ||||
|             mode: "same-origin", | ||||
|         this.apiRequest().then(() => { | ||||
|             this.setDone(SUCCESS_CLASS); | ||||
|         }) | ||||
|             .then((r) => { | ||||
|                 if (!r.ok) { | ||||
|                     throw r; | ||||
|                 } | ||||
|                 return r; | ||||
|             }) | ||||
|             .then(() => { | ||||
|                 this.setDone(SUCCESS_CLASS); | ||||
|             }) | ||||
|             .catch((e: Error | Response) => { | ||||
|                 if (e instanceof Error) { | ||||
|         .catch((e: Error | Response) => { | ||||
|             if (e instanceof Error) { | ||||
|                 showMessage({ | ||||
|                     level_tag: "error", | ||||
|                     message: e.toString() | ||||
|                 }); | ||||
|             } else { | ||||
|                 e.text().then(t => { | ||||
|                     showMessage({ | ||||
|                         level_tag: "error", | ||||
|                         message: e.toString() | ||||
|                         message: t | ||||
|                     }); | ||||
|                 } else { | ||||
|                     e.text().then(t => { | ||||
|                         showMessage({ | ||||
|                             level_tag: "error", | ||||
|                             message: t | ||||
|                         }); | ||||
|                     }); | ||||
|                 } | ||||
|                 this.setDone(ERROR_CLASS); | ||||
|             }); | ||||
|                 }); | ||||
|             } | ||||
|             this.setDone(ERROR_CLASS); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -3,9 +3,10 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu | ||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||
| // @ts-ignore | ||||
| import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||
| import { Token } from "../../api/Tokens"; | ||||
| import { CoreApi } from "../../api"; | ||||
| import { ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||
| import { ColorStyles } from "../../common/styles"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| @customElement("ak-token-copy-button") | ||||
| export class TokenCopyButton extends LitElement { | ||||
| @ -36,8 +37,14 @@ export class TokenCopyButton extends LitElement { | ||||
|             }, 1500); | ||||
|             return; | ||||
|         } | ||||
|         Token.getKey(this.identifier).then((token) => { | ||||
|             navigator.clipboard.writeText(token).then(() => { | ||||
|         new CoreApi(DEFAULT_CONFIG).coreTokensViewKey({ | ||||
|             identifier: this.identifier | ||||
|         }).then((token) => { | ||||
|             if (!token.key) { | ||||
|                 this.buttonClass = ERROR_CLASS; | ||||
|                 return; | ||||
|             } | ||||
|             navigator.clipboard.writeText(token.key).then(() => { | ||||
|                 this.buttonClass = SUCCESS_CLASS; | ||||
|                 setTimeout(() => { | ||||
|                     this.buttonClass = PRIMARY_CLASS; | ||||
|  | ||||
							
								
								
									
										41
									
								
								web/src/elements/charts/AdminLoginsChart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								web/src/elements/charts/AdminLoginsChart.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| import { customElement } from "lit-element"; | ||||
| import Chart from "chart.js"; | ||||
| import { AdminApi, LoginMetrics } from "../../api"; | ||||
| import { AKChart } from "./Chart"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| @customElement("ak-charts-admin-login") | ||||
| export class AdminLoginsChart extends AKChart<LoginMetrics> { | ||||
|  | ||||
|     apiRequest(): Promise<LoginMetrics> { | ||||
|         return new AdminApi(DEFAULT_CONFIG).adminMetricsList(); | ||||
|     } | ||||
|  | ||||
|     getDatasets(data: LoginMetrics): Chart.ChartDataSets[] { | ||||
|         return [ | ||||
|             { | ||||
|                 label: "Failed Logins", | ||||
|                 backgroundColor: "rgba(201, 25, 11, .5)", | ||||
|                 spanGaps: true, | ||||
|                 data: data.loginsFailedPer1h?.map((cord) => { | ||||
|                     return { | ||||
|                         x: cord.xCord, | ||||
|                         y: cord.yCord, | ||||
|                     }; | ||||
|                 }), | ||||
|             }, | ||||
|             { | ||||
|                 label: "Successful Logins", | ||||
|                 backgroundColor: "rgba(189, 229, 184, .5)", | ||||
|                 spanGaps: true, | ||||
|                 data: data.loginsPer1h?.map((cord) => { | ||||
|                     return { | ||||
|                         x: cord.xCord, | ||||
|                         y: cord.yCord, | ||||
|                     }; | ||||
|                 }), | ||||
|             }, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										32
									
								
								web/src/elements/charts/ApplicationAuthorizeChart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/src/elements/charts/ApplicationAuthorizeChart.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| import { customElement, property } from "lit-element"; | ||||
| import { Coordinate, CoreApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AKChart } from "./Chart"; | ||||
|  | ||||
| @customElement("ak-charts-application-authorize") | ||||
| export class ApplicationAuthorizeChart extends AKChart<Coordinate[]> { | ||||
|  | ||||
|     @property() | ||||
|     applicationSlug!: string; | ||||
|  | ||||
|     apiRequest(): Promise<Coordinate[]> { | ||||
|         return new CoreApi(DEFAULT_CONFIG).coreApplicationsMetrics({ slug: this.applicationSlug }); | ||||
|     } | ||||
|  | ||||
|     getDatasets(data: Coordinate[]): Chart.ChartDataSets[] { | ||||
|         return [ | ||||
|             { | ||||
|                 label: "Authorizations", | ||||
|                 backgroundColor: "rgba(189, 229, 184, .5)", | ||||
|                 spanGaps: true, | ||||
|                 data: data.map((cord) => { | ||||
|                     return { | ||||
|                         x: cord.xCord, | ||||
|                         y: cord.yCord, | ||||
|                     }; | ||||
|                 }), | ||||
|             }, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										103
									
								
								web/src/elements/charts/Chart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								web/src/elements/charts/Chart.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| import { css, CSSResult, html, LitElement, TemplateResult } from "lit-element"; | ||||
| import Chart from "chart.js"; | ||||
|  | ||||
| interface TickValue { | ||||
|     value: number; | ||||
|     major: boolean; | ||||
| } | ||||
|  | ||||
| export abstract class AKChart<T> extends LitElement { | ||||
|  | ||||
|     abstract apiRequest(): Promise<T>; | ||||
|     abstract getDatasets(data: T): Chart.ChartDataSets[]; | ||||
|  | ||||
|     chart?: Chart; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return [css` | ||||
|             :host { | ||||
|                 position: relative; | ||||
|                 height: 100%; | ||||
|                 width: 100%; | ||||
|                 display: block; | ||||
|                 min-height: 25rem; | ||||
|             } | ||||
|             canvas { | ||||
|                 width: 100px; | ||||
|                 height: 100px; | ||||
|             } | ||||
|         `]; | ||||
|     } | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         window.addEventListener("resize", () => { | ||||
|             if (this.chart) { | ||||
|                 this.chart.resize(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     configureChart(data: T, ctx: CanvasRenderingContext2D): Chart { | ||||
|         return new Chart(ctx, { | ||||
|             type: "bar", | ||||
|             data: { | ||||
|                 datasets: this.getDatasets(data), | ||||
|             }, | ||||
|             options: { | ||||
|                 maintainAspectRatio: false, | ||||
|                 spanGaps: true, | ||||
|                 scales: { | ||||
|                     xAxes: [ | ||||
|                         { | ||||
|                             stacked: true, | ||||
|                             gridLines: { | ||||
|                                 color: "rgba(0, 0, 0, 0)", | ||||
|                             }, | ||||
|                             type: "time", | ||||
|                             offset: true, | ||||
|                             ticks: { | ||||
|                                 callback: function (value, index: number, values) { | ||||
|                                     const valueStamp = <TickValue>(<unknown>values[index]); | ||||
|                                     const delta = Date.now() - valueStamp.value; | ||||
|                                     const ago = Math.round(delta / 1000 / 3600); | ||||
|                                     return `${ago} Hours ago`; | ||||
|                                 }, | ||||
|                                 autoSkip: true, | ||||
|                                 maxTicksLimit: 8, | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                     yAxes: [ | ||||
|                         { | ||||
|                             stacked: true, | ||||
|                             gridLines: { | ||||
|                                 color: "rgba(0, 0, 0, 0)", | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         this.apiRequest().then((r) => { | ||||
|             const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas"); | ||||
|             if (!canvas) { | ||||
|                 console.warn("Failed to get canvas element"); | ||||
|                 return false; | ||||
|             } | ||||
|             const ctx = canvas.getContext("2d"); | ||||
|             if (!ctx) { | ||||
|                 console.warn("failed to get 2d context"); | ||||
|                 return false; | ||||
|             } | ||||
|             this.chart = this.configureChart(r, ctx); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         return html`<canvas></canvas>`; | ||||
|     } | ||||
| } | ||||
| @ -35,6 +35,7 @@ export class MessageContainer extends LitElement { | ||||
|     } | ||||
|  | ||||
|     connect(): void { | ||||
|         if (navigator.webdriver) return; | ||||
|         const wsUrl = `${window.location.protocol.replace("http", "ws")}//${ | ||||
|             window.location.host | ||||
|         }/ws/client/`; | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| import { gettext } from "django"; | ||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { EventsApi, Notification } from "../../api"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { Notification } from "../../api/EventNotification"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| @customElement("ak-notification-drawer") | ||||
| @ -30,9 +31,9 @@ export class NotificationDrawer extends LitElement { | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         Notification.list({ | ||||
|             seen: false, | ||||
|             ordering: "-created" | ||||
|         new EventsApi(DEFAULT_CONFIG).eventsNotificationsList({ | ||||
|             seen: "false", | ||||
|             ordering: "-created", | ||||
|         }).then(r => { | ||||
|             this.notifications = r; | ||||
|             this.unread = r.results.length; | ||||
| @ -40,7 +41,6 @@ export class NotificationDrawer extends LitElement { | ||||
|     } | ||||
|  | ||||
|     renderItem(item: Notification): TemplateResult { | ||||
|         const created = new Date(parseInt(item.created, 10) * 1000); | ||||
|         let level = ""; | ||||
|         switch (item.severity) { | ||||
|         case "notice": | ||||
| @ -66,15 +66,18 @@ export class NotificationDrawer extends LitElement { | ||||
|             </div> | ||||
|             <div class="pf-c-notification-drawer__list-item-action"> | ||||
|                 <button class="pf-c-dropdown__toggle pf-m-plain" type="button" @click=${() => { | ||||
|                     Notification.markSeen(item.pk).then(() => { | ||||
|                         this.firstUpdated(); | ||||
|                     new EventsApi(DEFAULT_CONFIG).eventsNotificationsPartialUpdate({ | ||||
|                         uuid: item.pk || "", | ||||
|                         data: { | ||||
|                             seen: true, | ||||
|                         } | ||||
|                     }); | ||||
|                 }}> | ||||
|                     <i class="fas fa-times"></i> | ||||
|                 </button> | ||||
|             </div> | ||||
|             <p class="pf-c-notification-drawer__list-item-description">${item.body}</p> | ||||
|             <small class="pf-c-notification-drawer__list-item-timestamp">${created.toLocaleString()}</small> | ||||
|             <small class="pf-c-notification-drawer__list-item-timestamp">${item.created?.toLocaleString()}</small> | ||||
|         </li>`; | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -2,16 +2,16 @@ import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { Table, TableColumn } from "../../elements/table/Table"; | ||||
| import { PolicyBinding } from "../../api/PolicyBindings"; | ||||
| import { PoliciesApi, PolicyBinding } from "../../api"; | ||||
|  | ||||
| import "../../elements/Tabs"; | ||||
| import "../../elements/AdminLoginsChart"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import "../../elements/buttons/Dropdown"; | ||||
| import { Policy } from "../../api/Policies"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-bound-policies-list") | ||||
| export class BoundPoliciesList extends Table<PolicyBinding> { | ||||
| @ -19,11 +19,11 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | ||||
|     target?: string; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<PolicyBinding>> { | ||||
|         return PolicyBinding.list({ | ||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({ | ||||
|             target: this.target || "", | ||||
|             ordering: "order", | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @ -56,13 +56,13 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | ||||
|             html`${item.order}`, | ||||
|             html`${item.timeout}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${PolicyBinding.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.policyBindings(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${PolicyBinding.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.policyBindings(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -78,7 +78,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | ||||
|                 ${gettext("No policies are currently bound to this object.")} | ||||
|             </div> | ||||
|             <div slot="primary"> | ||||
|                 <ak-modal-button href=${PolicyBinding.adminUrl(`create/?target=${this.target}`)}> | ||||
|                 <ak-modal-button href=${AdminURLManager.policyBindings(`create/?target=${this.target}`)}> | ||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                         ${gettext("Bind Policy")} | ||||
|                     </ak-spinner-button> | ||||
| @ -96,7 +96,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | ||||
|                 <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> | ||||
|             </button> | ||||
|             <ul class="pf-c-dropdown__menu" hidden> | ||||
|                 ${until(Policy.getTypes().then((types) => { | ||||
|                 ${until(new PoliciesApi(DEFAULT_CONFIG).policiesAllTypes({}).then((types) => { | ||||
|                     return types.map((type) => { | ||||
|                         return html`<li> | ||||
|                             <ak-modal-button href="${type.link}"> | ||||
| @ -110,7 +110,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | ||||
|                 }), html`<ak-spinner></ak-spinner>`)} | ||||
|             </ul> | ||||
|         </ak-dropdown> | ||||
|         <ak-modal-button href=${PolicyBinding.adminUrl(`create/?target=${this.target}`)}> | ||||
|         <ak-modal-button href=${AdminURLManager.policyBindings(`create/?target=${this.target}`)}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Bind Policy")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -3,15 +3,17 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu | ||||
| import PageStyle from "@patternfly/patternfly/components/Page/page.css"; | ||||
| // @ts-ignore | ||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||
| import { Config } from "../../api/Config"; | ||||
| import { configureSentry } from "../../api/Config"; | ||||
| import { Config } from "../../api"; | ||||
| import { ifDefined } from "lit-html/directives/if-defined"; | ||||
|  | ||||
| export const DefaultConfig: Config = { | ||||
|     branding_logo: " /static/dist/assets/icons/icon_left_brand.svg", | ||||
|     branding_title: "authentik", | ||||
|     brandingLogo: " /static/dist/assets/icons/icon_left_brand.svg", | ||||
|     brandingTitle: "authentik", | ||||
|  | ||||
|     error_reporting_enabled: false, | ||||
|     error_reporting_environment: "", | ||||
|     error_reporting_send_pii: false, | ||||
|     errorReportingEnabled: false, | ||||
|     errorReportingEnvironment: "", | ||||
|     errorReportingSendPii: false, | ||||
| }; | ||||
|  | ||||
| @customElement("ak-sidebar-brand") | ||||
| @ -40,13 +42,13 @@ export class SidebarBrand extends LitElement { | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         Config.get().then((c) => (this.config = c)); | ||||
|         configureSentry().then((c) => {this.config = c;}); | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         return html` <a href="#/" class="pf-c-page__header-brand-link"> | ||||
|             <div class="pf-c-brand ak-brand"> | ||||
|                 <img src="${this.config.branding_logo}" alt="authentik icon" loading="lazy" /> | ||||
|                 <img src="${ifDefined(this.config.brandingLogo)}" alt="authentik icon" loading="lazy" /> | ||||
|             </div> | ||||
|         </a>`; | ||||
|     } | ||||
|  | ||||
| @ -5,10 +5,11 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css"; | ||||
| import fa from "@fortawesome/fontawesome-free/css/all.css"; | ||||
| // @ts-ignore | ||||
| import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css"; | ||||
| import { User } from "../../api/Users"; | ||||
| import { me } from "../../api/Users"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
|  | ||||
| import "../notifications/NotificationTrigger"; | ||||
| import { ifDefined } from "lit-html/directives/if-defined"; | ||||
|  | ||||
| @customElement("ak-sidebar-user") | ||||
| export class SidebarUser extends LitElement { | ||||
| @ -37,8 +38,8 @@ export class SidebarUser extends LitElement { | ||||
|     render(): TemplateResult { | ||||
|         return html` | ||||
|             <a href="#/-/user/" class="pf-c-nav__link user-avatar" id="user-settings"> | ||||
|                 ${until(User.me().then((u) => { | ||||
|                     return html`<img class="pf-c-avatar" src="${u.avatar}" alt="" />`; | ||||
|                 ${until(me().then((u) => { | ||||
|                     return html`<img class="pf-c-avatar" src="${ifDefined(u.avatar)}" alt="" />`; | ||||
|                 }), html``)} | ||||
|             </a> | ||||
|             <ak-notification-trigger class="pf-c-nav__link user-notifications"> | ||||
|  | ||||
| @ -5,7 +5,7 @@ import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| import "./TablePagination"; | ||||
| import "../EmptyState"; | ||||
|  | ||||
| import "../Spinner"; | ||||
|  | ||||
| export class TableColumn { | ||||
|  | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import { PBPagination } from "../../api/Client"; | ||||
| import { AKPagination } from "../../api/Client"; | ||||
| import { gettext } from "django"; | ||||
|  | ||||
| @customElement("ak-table-pagination") | ||||
| export class TablePagination extends LitElement { | ||||
|     @property({attribute: false}) | ||||
|     pages?: PBPagination; | ||||
|     pages?: AKPagination; | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     // eslint-disable-next-line | ||||
| @ -22,8 +22,8 @@ export class TablePagination extends LitElement { | ||||
|                 <div class="pf-c-options-menu"> | ||||
|                     <div class="pf-c-options-menu__toggle pf-m-text pf-m-plain"> | ||||
|                         <span class="pf-c-options-menu__toggle-text"> | ||||
|                             ${this.pages?.start_index} - | ||||
|                             ${this.pages?.end_index} of | ||||
|                             ${this.pages?.startIndex} - | ||||
|                             ${this.pages?.endIndex} of | ||||
|                             ${this.pages?.count} | ||||
|                         </span> | ||||
|                     </div> | ||||
|  | ||||
| @ -1,3 +1,3 @@ | ||||
| import "construct-style-sheets-polyfill"; | ||||
|  | ||||
| import "./pages/generic/FlowExecutor"; | ||||
| import "./flows/FlowExecutor"; | ||||
|  | ||||
| @ -1,34 +1,34 @@ | ||||
| import { gettext } from "django"; | ||||
| import { LitElement, html, customElement, property, TemplateResult, CSSResult, css } from "lit-element"; | ||||
| import { unsafeHTML } from "lit-html/directives/unsafe-html"; | ||||
| import { getCookie } from "../../utils"; | ||||
| import "../../elements/stages/authenticator_static/AuthenticatorStaticStage"; | ||||
| import "../../elements/stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||
| import "../../elements/stages/authenticator_validate/AuthenticatorValidateStage"; | ||||
| import "../../elements/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||
| import "../../elements/stages/autosubmit/AutosubmitStage"; | ||||
| import "../../elements/stages/captcha/CaptchaStage"; | ||||
| import "../../elements/stages/consent/ConsentStage"; | ||||
| import "../../elements/stages/email/EmailStage"; | ||||
| import "../../elements/stages/identification/IdentificationStage"; | ||||
| import "../../elements/stages/password/PasswordStage"; | ||||
| import "../../elements/stages/prompt/PromptStage"; | ||||
| import { ShellChallenge, Challenge, ChallengeTypes, Flow, RedirectChallenge } from "../../api/Flows"; | ||||
| import { DefaultClient } from "../../api/Client"; | ||||
| import { IdentificationChallenge } from "../../elements/stages/identification/IdentificationStage"; | ||||
| import { PasswordChallenge } from "../../elements/stages/password/PasswordStage"; | ||||
| import { ConsentChallenge } from "../../elements/stages/consent/ConsentStage"; | ||||
| import { EmailChallenge } from "../../elements/stages/email/EmailStage"; | ||||
| import { AutosubmitChallenge } from "../../elements/stages/autosubmit/AutosubmitStage"; | ||||
| import { PromptChallenge } from "../../elements/stages/prompt/PromptStage"; | ||||
| import { AuthenticatorTOTPChallenge } from "../../elements/stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||
| import { AuthenticatorStaticChallenge } from "../../elements/stages/authenticator_static/AuthenticatorStaticStage"; | ||||
| import { AuthenticatorValidateStageChallenge } from "../../elements/stages/authenticator_validate/AuthenticatorValidateStage"; | ||||
| import { WebAuthnAuthenticatorRegisterChallenge } from "../../elements/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||
| import { CaptchaChallenge } from "../../elements/stages/captcha/CaptchaStage"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import { SpinnerSize } from "../../elements/Spinner"; | ||||
| import { StageHost } from "../../elements/stages/base"; | ||||
| import "./stages/authenticator_static/AuthenticatorStaticStage"; | ||||
| import "./stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||
| import "./stages/authenticator_validate/AuthenticatorValidateStage"; | ||||
| import "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||
| import "./stages/autosubmit/AutosubmitStage"; | ||||
| import "./stages/captcha/CaptchaStage"; | ||||
| import "./stages/consent/ConsentStage"; | ||||
| import "./stages/email/EmailStage"; | ||||
| import "./stages/identification/IdentificationStage"; | ||||
| import "./stages/password/PasswordStage"; | ||||
| import "./stages/prompt/PromptStage"; | ||||
| import { ShellChallenge, RedirectChallenge } from "../api/Flows"; | ||||
| import { IdentificationChallenge } from "./stages/identification/IdentificationStage"; | ||||
| import { PasswordChallenge } from "./stages/password/PasswordStage"; | ||||
| import { ConsentChallenge } from "./stages/consent/ConsentStage"; | ||||
| import { EmailChallenge } from "./stages/email/EmailStage"; | ||||
| import { AutosubmitChallenge } from "./stages/autosubmit/AutosubmitStage"; | ||||
| import { PromptChallenge } from "./stages/prompt/PromptStage"; | ||||
| import { AuthenticatorTOTPChallenge } from "./stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||
| import { AuthenticatorStaticChallenge } from "./stages/authenticator_static/AuthenticatorStaticStage"; | ||||
| import { AuthenticatorValidateStageChallenge } from "./stages/authenticator_validate/AuthenticatorValidateStage"; | ||||
| import { WebAuthnAuthenticatorRegisterChallenge } from "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||
| import { CaptchaChallenge } from "./stages/captcha/CaptchaStage"; | ||||
| import { COMMON_STYLES } from "../common/styles"; | ||||
| import { SpinnerSize } from "../elements/Spinner"; | ||||
| import { StageHost } from "./stages/base"; | ||||
| import { Challenge, ChallengeTypeEnum, FlowsApi } from "../api"; | ||||
| import { DEFAULT_CONFIG } from "../api/Config"; | ||||
| 
 | ||||
| @customElement("ak-flow-executor") | ||||
| export class FlowExecutor extends LitElement implements StageHost { | ||||
| @ -68,37 +68,30 @@ export class FlowExecutor extends LitElement implements StageHost { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     submit(formData?: FormData): Promise<void> { | ||||
|         const csrftoken = getCookie("authentik_csrf"); | ||||
|         const request = new Request(DefaultClient.makeUrl(["flows", "executor", this.flowSlug]), { | ||||
|             headers: { | ||||
|                 "X-CSRFToken": csrftoken, | ||||
|             }, | ||||
|         }); | ||||
|     submit<T>(formData?: T): Promise<void> { | ||||
|         this.loading = true; | ||||
|         return fetch(request, { | ||||
|             method: "POST", | ||||
|             mode: "same-origin", | ||||
|             body: formData, | ||||
|         }) | ||||
|             .then((response) => { | ||||
|                 return response.json(); | ||||
|             }) | ||||
|             .then((data) => { | ||||
|                 this.challenge = data; | ||||
|             }) | ||||
|             .catch((e) => { | ||||
|                 this.errorMessage(e); | ||||
|             }) | ||||
|             .finally(() => { | ||||
|                 this.loading = false; | ||||
|             }); | ||||
|         return new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolveRaw({ | ||||
|             flowSlug: this.flowSlug, | ||||
|             data: formData || {}, | ||||
|         }).then((challengeRaw) => { | ||||
|             return challengeRaw.raw.json(); | ||||
|         }).then((data) => { | ||||
|             this.challenge = data; | ||||
|         }).catch((e) => { | ||||
|             this.errorMessage(e); | ||||
|         }).finally(() => { | ||||
|             this.loading = false; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     firstUpdated(): void { | ||||
|         this.loading = true; | ||||
|         Flow.executor(this.flowSlug).then((challenge) => { | ||||
|             this.challenge = challenge; | ||||
|         new FlowsApi(DEFAULT_CONFIG).flowsExecutorGetRaw({ | ||||
|             flowSlug: this.flowSlug | ||||
|         }).then((challengeRaw) => { | ||||
|             return challengeRaw.raw.json(); | ||||
|         }).then((challenge) => { | ||||
|             this.challenge = challenge as Challenge; | ||||
|         }).catch((e) => { | ||||
|             // Catch JSON or Update errors
 | ||||
|             this.errorMessage(e); | ||||
| @ -109,7 +102,7 @@ export class FlowExecutor extends LitElement implements StageHost { | ||||
| 
 | ||||
|     errorMessage(error: string): void { | ||||
|         this.challenge = <ShellChallenge>{ | ||||
|             type: ChallengeTypes.shell, | ||||
|             type: ChallengeTypeEnum.Shell, | ||||
|             body: `<style>
 | ||||
|                     .ak-exception { | ||||
|                         font-family: monospace; | ||||
| @ -139,13 +132,13 @@ export class FlowExecutor extends LitElement implements StageHost { | ||||
|             return this.renderLoading(); | ||||
|         } | ||||
|         switch (this.challenge.type) { | ||||
|             case ChallengeTypes.redirect: | ||||
|             case ChallengeTypeEnum.Redirect: | ||||
|                 console.debug(`authentik/flows: redirecting to ${(this.challenge as RedirectChallenge).to}`); | ||||
|                 window.location.assign((this.challenge as RedirectChallenge).to); | ||||
|                 return this.renderLoading(); | ||||
|             case ChallengeTypes.shell: | ||||
|             case ChallengeTypeEnum.Shell: | ||||
|                 return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`; | ||||
|             case ChallengeTypes.native: | ||||
|             case ChallengeTypeEnum.Native: | ||||
|                 switch (this.challenge.component) { | ||||
|                     case "ak-stage-identification": | ||||
|                         return html`<ak-stage-identification .host=${this} .challenge=${this.challenge as IdentificationChallenge}></ak-stage-identification>`; | ||||
| @ -4,6 +4,7 @@ import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../form"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| export interface AuthenticatorStaticChallenge extends WithUserInfoChallenge { | ||||
|     codes: number[]; | ||||
| @ -5,7 +5,8 @@ import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "webcomponent-qr-code"; | ||||
| import "../form"; | ||||
| import { showMessage } from "../../messages/MessageContainer"; | ||||
| import { showMessage } from "../../../elements/messages/MessageContainer"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| export interface AuthenticatorTOTPChallenge extends WithUserInfoChallenge { | ||||
|     config_url: string; | ||||
| @ -36,8 +36,8 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost { | ||||
|     @property({attribute: false}) | ||||
|     selectedDeviceChallenge?: DeviceChallenge; | ||||
| 
 | ||||
|     submit(formData?: FormData): Promise<void> { | ||||
|         return this.host?.submit(formData) || Promise.resolve(); | ||||
|     submit<T>(formData?: T): Promise<void> { | ||||
|         return this.host?.submit<T>(formData) || Promise.resolve(); | ||||
|     } | ||||
| 
 | ||||
|     static get styles(): CSSResult[] { | ||||
| @ -4,6 +4,7 @@ import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage"; | ||||
| import "../form"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| @customElement("ak-stage-authenticator-validate-code") | ||||
| export class AuthenticatorValidateStageWebCode extends BaseStage { | ||||
| @ -1,7 +1,7 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { SpinnerSize } from "../../Spinner"; | ||||
| import { SpinnerSize } from "../../../elements/Spinner"; | ||||
| import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils"; | ||||
| import { BaseStage } from "../base"; | ||||
| import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage"; | ||||
| @ -1,7 +1,7 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||
| import { SpinnerSize } from "../../Spinner"; | ||||
| import { SpinnerSize } from "../../../elements/Spinner"; | ||||
| import { BaseStage } from "../base"; | ||||
| import { Assertion, transformCredentialCreateOptions, transformNewAssertionForServer } from "./utils"; | ||||
| 
 | ||||
| @ -3,7 +3,8 @@ import { CSSResult, customElement, html, property, TemplateResult } from "lit-el | ||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../../Spinner"; | ||||
| import "../../../elements/Spinner"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| export interface AutosubmitChallenge extends WithUserInfoChallenge { | ||||
|     url: string; | ||||
| @ -1,7 +1,7 @@ | ||||
| import { LitElement } from "lit-element"; | ||||
| 
 | ||||
| export interface StageHost { | ||||
|     submit(formData?: FormData): Promise<void>; | ||||
|     submit<T>(formData?: T): Promise<void>; | ||||
| } | ||||
| 
 | ||||
| export class BaseStage extends LitElement { | ||||
| @ -10,8 +10,12 @@ export class BaseStage extends LitElement { | ||||
| 
 | ||||
|     submitForm(e: Event): void { | ||||
|         e.preventDefault(); | ||||
|         const object: { | ||||
|             [key: string]: unknown; | ||||
|         } = {}; | ||||
|         const form = new FormData(this.shadowRoot?.querySelector("form") || undefined); | ||||
|         this.host?.submit(form); | ||||
|         form.forEach((value, key) => object[key] = value); | ||||
|         this.host?.submit(object); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -2,9 +2,10 @@ import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { SpinnerSize } from "../../Spinner"; | ||||
| import { SpinnerSize } from "../../../elements/Spinner"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../form"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| export interface CaptchaChallenge extends WithUserInfoChallenge { | ||||
|     site_key: string; | ||||
| @ -3,6 +3,7 @@ import { CSSResult, customElement, html, property, TemplateResult } from "lit-el | ||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| export interface Permission { | ||||
|     name: string; | ||||
| @ -1,10 +1,11 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { Challenge } from "../../../api/Flows"; | ||||
| import { Challenge } from "../../../api"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| export type EmailChallenge = Challenge | ||||
| export type EmailChallenge = Challenge; | ||||
| 
 | ||||
| @customElement("ak-stage-email") | ||||
| export class EmailStage extends BaseStage { | ||||
| @ -1,9 +1,10 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { Challenge } from "../../../api/Flows"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../form"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| import { Challenge } from "../../../api/Flows"; | ||||
| 
 | ||||
| export interface IdentificationChallenge extends Challenge { | ||||
| 
 | ||||
| @ -4,6 +4,7 @@ import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../form"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| 
 | ||||
| export interface PasswordChallenge extends WithUserInfoChallenge { | ||||
|     recovery_url?: string; | ||||
| @ -1,10 +1,11 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { unsafeHTML } from "lit-html/directives/unsafe-html"; | ||||
| import { Challenge } from "../../../api/Flows"; | ||||
| import { COMMON_STYLES } from "../../../common/styles"; | ||||
| import { BaseStage } from "../base"; | ||||
| import "../form"; | ||||
| import "../../../elements/utils/LoadingState"; | ||||
| import { Challenge } from "../../../api/Flows"; | ||||
| 
 | ||||
| export interface Prompt { | ||||
|     field_key: string; | ||||
| @ -1,5 +1,5 @@ | ||||
| import { customElement } from "lit-element"; | ||||
| import { User } from "../api/Users"; | ||||
| import { me } from "../api/Users"; | ||||
| import { SidebarItem } from "../elements/sidebar/Sidebar"; | ||||
| import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "../elements/router/Route"; | ||||
| import { Interface } from "./Interface"; | ||||
| @ -10,7 +10,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | ||||
|         new SidebarItem("Overview", "/administration/overview"), | ||||
|         new SidebarItem("System Tasks", "/administration/system-tasks"), | ||||
|     ).when((): Promise<boolean> => { | ||||
|         return User.me().then(u => u.is_superuser); | ||||
|         return me().then(u => u.isSuperuser||false); | ||||
|     }), | ||||
|     new SidebarItem("Events").children( | ||||
|         new SidebarItem("Log", "/events/log").activeWhen( | ||||
| @ -19,7 +19,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | ||||
|         new SidebarItem("Notification Rules", "/events/rules"), | ||||
|         new SidebarItem("Notification Transports", "/events/transports"), | ||||
|     ).when((): Promise<boolean> => { | ||||
|         return User.me().then(u => u.is_superuser); | ||||
|         return me().then(u => u.isSuperuser||false); | ||||
|     }), | ||||
|     new SidebarItem("Resources").children( | ||||
|         new SidebarItem("Applications", "/core/applications").activeWhen( | ||||
| @ -34,13 +34,13 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | ||||
|         new SidebarItem("Outposts", "/outpost/outposts"), | ||||
|         new SidebarItem("Outpost Service Connections", "/outpost/service-connections"), | ||||
|     ).when((): Promise<boolean> => { | ||||
|         return User.me().then(u => u.is_superuser); | ||||
|         return me().then(u => u.isSuperuser||false); | ||||
|     }), | ||||
|     new SidebarItem("Customisation").children( | ||||
|         new SidebarItem("Policies", "/policy/policies"), | ||||
|         new SidebarItem("Property Mappings", "/core/property-mappings"), | ||||
|     ).when((): Promise<boolean> => { | ||||
|         return User.me().then(u => u.is_superuser); | ||||
|         return me().then(u => u.isSuperuser||false); | ||||
|     }), | ||||
|     new SidebarItem("Flows").children( | ||||
|         new SidebarItem("Flows", "/flow/flows").activeWhen(`^/flow/flows/(?<slug>${SLUG_REGEX})$`), | ||||
| @ -48,7 +48,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | ||||
|         new SidebarItem("Prompts", "/flow/stages/prompts"), | ||||
|         new SidebarItem("Invitations", "/flow/stages/invitations"), | ||||
|     ).when((): Promise<boolean> => { | ||||
|         return User.me().then(u => u.is_superuser); | ||||
|         return me().then(u => u.isSuperuser||false); | ||||
|     }), | ||||
|     new SidebarItem("Identity & Cryptography").children( | ||||
|         new SidebarItem("User", "/identity/users"), | ||||
| @ -56,7 +56,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | ||||
|         new SidebarItem("Certificates", "/crypto/certificates"), | ||||
|         new SidebarItem("Tokens", "/core/tokens"), | ||||
|     ).when((): Promise<boolean> => { | ||||
|         return User.me().then(u => u.is_superuser); | ||||
|         return me().then(u => u.isSuperuser||false); | ||||
|     }), | ||||
| ]; | ||||
|  | ||||
|  | ||||
| @ -1,34 +1,13 @@ | ||||
| import "construct-style-sheets-polyfill"; | ||||
|  | ||||
| // Elements that are used by SiteShell pages | ||||
| // And can't dynamically be imported | ||||
| import "./elements/buttons/ActionButton"; | ||||
| import "./elements/buttons/Dropdown"; | ||||
| import "./elements/buttons/ModalButton"; | ||||
| import "./elements/buttons/SpinnerButton"; | ||||
| import "./elements/buttons/TokenCopyButton"; | ||||
|  | ||||
| import "./elements/sidebar/Sidebar"; | ||||
| import "./elements/sidebar/SidebarBrand"; | ||||
| import "./elements/sidebar/SidebarUser"; | ||||
|  | ||||
| import "./elements/table/TablePagination"; | ||||
|  | ||||
| import "./elements/AdminLoginsChart"; | ||||
| import "./elements/EmptyState"; | ||||
| import "./elements/cards/AggregateCard"; | ||||
| import "./elements/cards/AggregatePromiseCard"; | ||||
| import "./elements/CodeMirror"; | ||||
| import "./elements/messages/MessageContainer"; | ||||
| import "./elements/Spinner"; | ||||
| import "./elements/Tabs"; | ||||
| import "./elements/router/RouterOutlet"; | ||||
|  | ||||
| import "./pages/generic/SiteShell"; | ||||
|  | ||||
| import "./pages/admin-overview/AdminOverviewPage"; | ||||
| import "./pages/admin-overview/TopApplicationsTable"; | ||||
| import "./pages/applications/ApplicationListPage"; | ||||
| import "./pages/applications/ApplicationViewPage"; | ||||
| import "./pages/tokens/UserTokenList"; | ||||
| import "./pages/LibraryPage"; | ||||
|  | ||||
| import "./pages/generic/SiteShell"; | ||||
| import "./interfaces/AdminInterface"; | ||||
|  | ||||
| @ -1,8 +1,9 @@ | ||||
| import { gettext } from "django"; | ||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { ifDefined } from "lit-html/directives/if-defined"; | ||||
| import { Application } from "../api/Applications"; | ||||
| import { Application, CoreApi } from "../api"; | ||||
| import { AKResponse } from "../api/Client"; | ||||
| import { DEFAULT_CONFIG } from "../api/Config"; | ||||
| import { COMMON_STYLES } from "../common/styles"; | ||||
| import { loading, truncate } from "../utils"; | ||||
|  | ||||
| @ -31,19 +32,19 @@ export class LibraryApplication extends LitElement { | ||||
|         if (!this.application) { | ||||
|             return html`<ak-spinner></ak-spinner>`; | ||||
|         } | ||||
|         return html` <a href="${this.application.launch_url}" class="pf-c-card pf-m-hoverable pf-m-compact"> | ||||
|         return html` <a href="${ifDefined(this.application.launchUrl)}" class="pf-c-card pf-m-hoverable pf-m-compact"> | ||||
|             <div class="pf-c-card__header"> | ||||
|                 ${this.application.meta_icon | ||||
|                     ? html`<img class="app-icon pf-c-avatar" src="${ifDefined(this.application.meta_icon)}" alt="Application Icon"/>` | ||||
|                 ${this.application.metaIcon | ||||
|                     ? html`<img class="app-icon pf-c-avatar" src="${ifDefined(this.application.metaIcon)}" alt="Application Icon"/>` | ||||
|                     : html`<i class="pf-icon pf-icon-arrow"></i>`} | ||||
|             </div> | ||||
|             <div class="pf-c-card__title"> | ||||
|                 <p id="card-1-check-label">${this.application.name}</p> | ||||
|                 <div class="pf-c-content"> | ||||
|                     <small>${this.application.meta_publisher}</small> | ||||
|                     <small>${this.application.metaPublisher}</small> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="pf-c-card__body">${truncate(this.application.meta_description, 35)}</div> | ||||
|             <div class="pf-c-card__body">${truncate(this.application.metaDescription, 35)}</div> | ||||
|         </a>`; | ||||
|     } | ||||
|  | ||||
| @ -64,7 +65,9 @@ export class LibraryPage extends LitElement { | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         Application.list().then((r) => (this.apps = r)); | ||||
|         new CoreApi(DEFAULT_CONFIG).coreApplicationsList({}).then((apps) => { | ||||
|             this.apps = apps; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     renderEmptyState(): TemplateResult { | ||||
|  | ||||
| @ -2,7 +2,7 @@ import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| import "../../elements/AdminLoginsChart"; | ||||
| import "../../elements/charts/AdminLoginsChart"; | ||||
| import "../../elements/cards/AggregatePromiseCard"; | ||||
| import "./TopApplicationsTable"; | ||||
| import "./cards/AdminStatusCard"; | ||||
| @ -30,7 +30,7 @@ export class AdminOverviewPage extends LitElement { | ||||
|         <section class="pf-c-page__main-section"> | ||||
|             <div class="pf-l-gallery pf-m-gutter"> | ||||
|                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Logins over the last 24 hours" style="grid-column-end: span 3;grid-row-end: span 2;"> | ||||
|                     <ak-admin-logins-chart .url="${["admin", "metrics"]}"></ak-admin-logins-chart> | ||||
|                     <ak-charts-admin-login></ak-charts-admin-login> | ||||
|                 </ak-aggregate-card> | ||||
|                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;"> | ||||
|                     <ak-top-applications-table></ak-top-applications-table> | ||||
|  | ||||
| @ -1,34 +1,39 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { Event, TopNEvent } from "../../api/Events"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import { EventsApi, EventTopPerUser } from "../../api"; | ||||
|  | ||||
| import "../../elements/Spinner"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| @customElement("ak-top-applications-table") | ||||
| export class TopApplicationsTable extends LitElement { | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     topN?: TopNEvent[]; | ||||
|     topN?: EventTopPerUser[]; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES; | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         Event.topForUser("authorize_application").then(events => this.topN = events); | ||||
|         new EventsApi(DEFAULT_CONFIG).eventsEventsTopPerUser({ | ||||
|             action: "authorize_application", | ||||
|         }).then((events) => { | ||||
|             this.topN = events; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     renderRow(event: TopNEvent): TemplateResult { | ||||
|     renderRow(event: EventTopPerUser): TemplateResult { | ||||
|         return html`<tr role="row"> | ||||
|             <td role="cell"> | ||||
|                 ${event.application.name} | ||||
|             </td> | ||||
|             <td role="cell"> | ||||
|                 ${event.counted_events} | ||||
|                 ${event.countedEvents} | ||||
|             </td> | ||||
|             <td role="cell"> | ||||
|                 <progress value="${event.counted_events}" max="${this.topN ? this.topN[0].counted_events : 0}"></progress> | ||||
|                 <progress value="${event.countedEvents}" max="${this.topN ? this.topN[0].countedEvents : 0}"></progress> | ||||
|             </td> | ||||
|         </tr>`; | ||||
|     } | ||||
|  | ||||
| @ -23,14 +23,14 @@ export abstract class AdminStatusCard<T> extends AggregateCard { | ||||
|     renderInner(): TemplateResult { | ||||
|         return html`<p class="center-value"> | ||||
|             ${until(this.getPrimaryValue().then((v) => { | ||||
|         this.value = v; | ||||
|         return this.getStatus(v); | ||||
|     }).then((status) => { | ||||
|         return html`<p class="ak-aggregate-card"> | ||||
|                     <i class="${status.icon}"></i> ${this.renderValue()} | ||||
|                 </p> | ||||
|                 ${status.message ? html`<p class="subtext">${status.message}</p>` : html``}`; | ||||
|     }), html`<ak-spinner size="${SpinnerSize.Large}"></ak-spinner>`)} | ||||
|                 this.value = v; | ||||
|                 return this.getStatus(v); | ||||
|             }).then((status) => { | ||||
|                 return html`<p class="ak-aggregate-card"> | ||||
|                         <i class="${status.icon}"></i> ${this.renderValue()} | ||||
|                     </p> | ||||
|                     ${status.message ? html`<p class="subtext">${status.message}</p>` : html``}`; | ||||
|             }), html`<ak-spinner size="${SpinnerSize.Large}"></ak-spinner>`)} | ||||
|         </p>`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,14 +1,17 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, TemplateResult } from "lit-element"; | ||||
| import { Flow } from "../../../api/Flows"; | ||||
| import { AdminStatus, AdminStatusCard } from "./AdminStatusCard"; | ||||
| import "../../../elements/buttons/ModalButton"; | ||||
| import { FlowsApi } from "../../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
|  | ||||
| @customElement("ak-admin-status-card-flow-cache") | ||||
| export class FlowCacheStatusCard extends AdminStatusCard<number> { | ||||
|  | ||||
|     getPrimaryValue(): Promise<number> { | ||||
|         return Flow.cached(); | ||||
|         return new FlowsApi(DEFAULT_CONFIG).flowsInstancesCached({}).then((value) => { | ||||
|             return value.count || 0; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     getStatus(value: number): Promise<AdminStatus> { | ||||
|  | ||||
| @ -1,15 +1,18 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement } from "lit-element"; | ||||
| import { TemplateResult, html } from "lit-html"; | ||||
| import { Policy } from "../../../api/Policies"; | ||||
| import { AdminStatusCard, AdminStatus } from "./AdminStatusCard"; | ||||
| import "../../../elements/buttons/ModalButton"; | ||||
| import { PoliciesApi } from "../../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
|  | ||||
| @customElement("ak-admin-status-card-policy-cache") | ||||
| export class PolicyCacheStatusCard extends AdminStatusCard<number> { | ||||
|  | ||||
|     getPrimaryValue(): Promise<number> { | ||||
|         return Policy.cached(); | ||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesAllCached({}).then((value) => { | ||||
|             return value.count || 0; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     getStatus(value: number): Promise<AdminStatus> { | ||||
|  | ||||
| @ -1,17 +1,18 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement } from "lit-element"; | ||||
| import { Policy } from "../../../api/Policies"; | ||||
| import { PoliciesApi } from "../../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
| import { AdminStatusCard, AdminStatus } from "./AdminStatusCard"; | ||||
|  | ||||
| @customElement("ak-admin-status-card-policy-unbound") | ||||
| export class PolicyUnboundStatusCard extends AdminStatusCard<number> { | ||||
|  | ||||
|     getPrimaryValue(): Promise<number> { | ||||
|         return Policy.list({ | ||||
|             "bindings__isnull": true, | ||||
|             "promptstage__isnull": true, | ||||
|         }).then((response) => { | ||||
|             return response.pagination.count; | ||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesAllList({ | ||||
|             bindingsIsnull: "true", | ||||
|             promptstageIsnull: "true", | ||||
|         }).then((value) => { | ||||
|             return value.pagination.count; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -1,16 +1,17 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement } from "lit-element"; | ||||
| import { Provider } from "../../../api/Providers"; | ||||
| import { ProvidersApi } from "../../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
| import { AdminStatusCard, AdminStatus } from "./AdminStatusCard"; | ||||
|  | ||||
| @customElement("ak-admin-status-card-provider") | ||||
| export class ProviderStatusCard extends AdminStatusCard<number> { | ||||
|  | ||||
|     getPrimaryValue(): Promise<number> { | ||||
|         return Provider.list({ | ||||
|             "application__isnull": true | ||||
|         }).then((response) => { | ||||
|             return response.pagination.count; | ||||
|         return new ProvidersApi(DEFAULT_CONFIG).providersAllList({ | ||||
|             applicationIsnull: "true" | ||||
|         }).then((value) => { | ||||
|             return value.pagination.count; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -1,12 +1,17 @@ | ||||
| import { customElement } from "lit-element"; | ||||
| import { User } from "../../../api/Users"; | ||||
| import { CoreApi } from "../../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
| import { AdminStatusCard, AdminStatus } from "./AdminStatusCard"; | ||||
|  | ||||
| @customElement("ak-admin-status-card-user-count") | ||||
| export class UserCountStatusCard extends AdminStatusCard<number> { | ||||
|  | ||||
|     getPrimaryValue(): Promise<number> { | ||||
|         return User.count(); | ||||
|         return new CoreApi(DEFAULT_CONFIG).coreUsersList({ | ||||
|             pageSize: 1 | ||||
|         }).then((value) => { | ||||
|             return value.pagination.count; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||||
|  | ||||
| @ -1,20 +1,21 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, TemplateResult } from "lit-element"; | ||||
| import { Version } from "../../../api/Versions"; | ||||
| import { AdminApi, Version } from "../../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
| import { AdminStatusCard, AdminStatus } from "./AdminStatusCard"; | ||||
|  | ||||
| @customElement("ak-admin-status-version") | ||||
| export class VersionStatusCard extends AdminStatusCard<Version> { | ||||
|  | ||||
|     getPrimaryValue(): Promise<Version> { | ||||
|         return Version.get(); | ||||
|         return new AdminApi(DEFAULT_CONFIG).adminVersionList({}); | ||||
|     } | ||||
|  | ||||
|     getStatus(value: Version): Promise<AdminStatus> { | ||||
|         if (value.outdated) { | ||||
|             return Promise.resolve<AdminStatus>({ | ||||
|                 icon: "fa fa-exclamation-triangle pf-m-warning", | ||||
|                 message: gettext(`${value.version_latest} is available!`), | ||||
|                 message: gettext(`${value.versionLatest} is available!`), | ||||
|             }); | ||||
|         } else { | ||||
|             return Promise.resolve<AdminStatus>({ | ||||
| @ -25,7 +26,7 @@ export class VersionStatusCard extends AdminStatusCard<Version> { | ||||
|     } | ||||
|  | ||||
|     renderValue(): TemplateResult { | ||||
|         return html`${this.value?.version_current}`; | ||||
|         return html`${this.value?.versionCurrent}`; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -1,14 +1,15 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement } from "lit-element"; | ||||
| import { DefaultClient, AKResponse } from "../../../api/Client"; | ||||
| import { AdminApi } from "../../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
| import { AdminStatus, AdminStatusCard } from "./AdminStatusCard"; | ||||
|  | ||||
| @customElement("ak-admin-status-card-workers") | ||||
| export class WorkersStatusCard extends AdminStatusCard<number> { | ||||
|  | ||||
|     getPrimaryValue(): Promise<number> { | ||||
|         return DefaultClient.fetch<AKResponse<number>>(["admin", "workers"]).then((r) => { | ||||
|             return r.pagination.count; | ||||
|         return new AdminApi(DEFAULT_CONFIG).adminWorkersList({}).then((workers) => { | ||||
|             return workers.pagination.count; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { Application } from "../../api/Applications"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| @ -8,6 +7,9 @@ import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { Application, CoreApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-application-list") | ||||
| export class ApplicationListPage extends TablePage<Application> { | ||||
| @ -28,10 +30,10 @@ export class ApplicationListPage extends TablePage<Application> { | ||||
|     order = "name"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Application>> { | ||||
|         return Application.list({ | ||||
|         return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -49,26 +51,26 @@ export class ApplicationListPage extends TablePage<Application> { | ||||
|  | ||||
|     row(item: Application): TemplateResult[] { | ||||
|         return [ | ||||
|             item.meta_icon ? | ||||
|                 html`<img class="app-icon pf-c-avatar" src="${item.meta_icon}" alt="${gettext("Application Icon")}">` : | ||||
|             item.metaIcon ? | ||||
|                 html`<img class="app-icon pf-c-avatar" src="${item.metaIcon}" alt="${gettext("Application Icon")}">` : | ||||
|                 html`<i class="pf-icon pf-icon-arrow"></i>`, | ||||
|             html`<a href="#/core/applications/${item.slug}"> | ||||
|                 <div> | ||||
|                     ${item.name} | ||||
|                 </div> | ||||
|                 ${item.meta_publisher ? html`<small>${item.meta_publisher}</small>` : html``} | ||||
|                 ${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``} | ||||
|             </a>`, | ||||
|             html`<code>${item.slug}</code>`, | ||||
|             html`${item.provider?.name}`, | ||||
|             html`${item.provider?.verbose_name}`, | ||||
|             html`${item.provider?.verboseName}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Application.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.applications(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Application.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.applications(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -80,7 +82,7 @@ export class ApplicationListPage extends TablePage<Application> { | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${Application.adminUrl("create/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.applications("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -1,14 +1,15 @@ | ||||
| import { gettext } from "django"; | ||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { Application } from "../../api/Applications"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| import "../../elements/Tabs"; | ||||
| import "../../elements/AdminLoginsChart"; | ||||
| import "../../elements/charts/ApplicationAuthorizeChart"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import "../../elements/policies/BoundPoliciesList"; | ||||
| import "../../elements/utils/LoadingState"; | ||||
| import { Application, CoreApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| @customElement("ak-application-view") | ||||
| export class ApplicationViewPage extends LitElement { | ||||
| @ -19,11 +20,15 @@ export class ApplicationViewPage extends LitElement { | ||||
|  | ||||
|     @property() | ||||
|     set applicationSlug(value: string) { | ||||
|         Application.get(value).then((app) => (this.application = app)); | ||||
|         new CoreApi(DEFAULT_CONFIG).coreApplicationsRead({ | ||||
|             slug: value | ||||
|         }).then((app) => { | ||||
|             this.application = app; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     application?: Application; | ||||
|     application!: Application; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES.concat( | ||||
| @ -52,10 +57,10 @@ export class ApplicationViewPage extends LitElement { | ||||
|         return html`<section class="pf-c-page__main-section pf-m-light"> | ||||
|                 <div class="pf-c-content"> | ||||
|                     <h1> | ||||
|                         <img class="pf-icon" src="${this.application?.meta_icon || ""}" /> | ||||
|                         <img class="pf-icon" src="${this.application?.metaIcon || ""}" /> | ||||
|                         ${this.application?.name} | ||||
|                     </h1> | ||||
|                     <p>${this.application?.meta_publisher}</p> | ||||
|                     <p>${this.application?.metaPublisher}</p> | ||||
|                 </div> | ||||
|             </section> | ||||
|             <ak-tabs> | ||||
| @ -69,9 +74,8 @@ export class ApplicationViewPage extends LitElement { | ||||
|                             </div> | ||||
|                             <div class="pf-c-card__body"> | ||||
|                                 ${this.application ? html` | ||||
|                                     <ak-admin-logins-chart | ||||
|                                         .url="${["core", "applications", this.application?.slug, "metrics"]}"> | ||||
|                                     </ak-admin-logins-chart>`: ""} | ||||
|                                     <ak-charts-application-authorize applicationSlug=${this.application.slug}> | ||||
|                                     </ak-charts-application-authorize>`: ""} | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-2-col"> | ||||
|  | ||||
| @ -3,11 +3,14 @@ import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| import { CryptoApi, CertificateKeyPair } from "../../api"; | ||||
|  | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { CertificateKeyPair } from "../../api/CertificateKeyPair"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| @customElement("ak-crypto-certificatekeypair-list") | ||||
| export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> { | ||||
| @ -30,10 +33,10 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> { | ||||
|     order = "name"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<CertificateKeyPair>> { | ||||
|         return CertificateKeyPair.list({ | ||||
|         return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -50,16 +53,16 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> { | ||||
|     row(item: CertificateKeyPair): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`${gettext(item.private_key_available ? "Yes" : "No")}`, | ||||
|             html`${new Date(item.cert_expiry * 1000).toLocaleString()}`, | ||||
|             html`${gettext(item.privateKeyAvailable ? "Yes" : "No")}`, | ||||
|             html`${item.certExpiry?.toLocaleString()}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${CertificateKeyPair.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.cryptoCertificates(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${CertificateKeyPair.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.cryptoCertificates(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -73,24 +76,24 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> { | ||||
|         return html` | ||||
|         <td role="cell" colspan="3"> | ||||
|             <div class="pf-c-table__expandable-row-content"> | ||||
|                     <dl class="pf-c-description-list pf-m-horizontal"> | ||||
|                         <div class="pf-c-description-list__group"> | ||||
|                             <dt class="pf-c-description-list__term"> | ||||
|                                 <span class="pf-c-description-list__text">${gettext("Certificate Fingerprint")}</span> | ||||
|                             </dt> | ||||
|                             <dd class="pf-c-description-list__description"> | ||||
|                                 <div class="pf-c-description-list__text">${item.fingerprint}</div> | ||||
|                             </dd> | ||||
|                         </div> | ||||
|                         <div class="pf-c-description-list__group"> | ||||
|                             <dt class="pf-c-description-list__term"> | ||||
|                                 <span class="pf-c-description-list__text">${gettext("Certificate Subjet")}</span> | ||||
|                             </dt> | ||||
|                             <dd class="pf-c-description-list__description"> | ||||
|                                 <div class="pf-c-description-list__text">${item.cert_subject}</div> | ||||
|                             </dd> | ||||
|                         </div> | ||||
|                     </dl> | ||||
|                 <dl class="pf-c-description-list pf-m-horizontal"> | ||||
|                     <div class="pf-c-description-list__group"> | ||||
|                         <dt class="pf-c-description-list__term"> | ||||
|                             <span class="pf-c-description-list__text">${gettext("Certificate Fingerprint")}</span> | ||||
|                         </dt> | ||||
|                         <dd class="pf-c-description-list__description"> | ||||
|                             <div class="pf-c-description-list__text">${item.fingerprint}</div> | ||||
|                         </dd> | ||||
|                     </div> | ||||
|                     <div class="pf-c-description-list__group"> | ||||
|                         <dt class="pf-c-description-list__term"> | ||||
|                             <span class="pf-c-description-list__text">${gettext("Certificate Subjet")}</span> | ||||
|                         </dt> | ||||
|                         <dd class="pf-c-description-list__description"> | ||||
|                             <div class="pf-c-description-list__text">${item.certSubject}</div> | ||||
|                         </dd> | ||||
|                     </div> | ||||
|                 </dl> | ||||
|             </div> | ||||
|         </td> | ||||
|         <td></td> | ||||
| @ -99,13 +102,13 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> { | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${CertificateKeyPair.adminUrl("create/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.cryptoCertificates("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
|             <div slot="modal"></div> | ||||
|         </ak-modal-button> | ||||
|         <ak-modal-button href=${CertificateKeyPair.adminUrl("generate/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.cryptoCertificates("generate/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                 ${gettext("Generate")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -1,18 +1,19 @@ | ||||
| import { gettext } from "django"; | ||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { Event, EventContext } from "../../api/Events"; | ||||
| import { Flow } from "../../api/Flows"; | ||||
| import { FlowsApi } from "../../api"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import "../../elements/Spinner"; | ||||
| import "../../elements/Expand"; | ||||
| import { SpinnerSize } from "../../elements/Spinner"; | ||||
| import { EventContext, EventWithContext } from "../../api/Events"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| @customElement("ak-event-info") | ||||
| export class EventInfo extends LitElement { | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     event?: Event; | ||||
|     event!: EventWithContext; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES.concat( | ||||
| @ -73,15 +74,15 @@ export class EventInfo extends LitElement { | ||||
|  | ||||
|     defaultResponse(): TemplateResult { | ||||
|         return html`<div class="pf-l-flex"> | ||||
|                     <div class="pf-l-flex__item"> | ||||
|                         <h3>${gettext("Context")}</h3> | ||||
|                         <code>${JSON.stringify(this.event?.context, null, 4)}</code> | ||||
|                     </div> | ||||
|                     <div class="pf-l-flex__item"> | ||||
|                         <h3>${gettext("User")}</h3> | ||||
|                         <code>${JSON.stringify(this.event?.user, null, 4)}</code> | ||||
|                     </div> | ||||
|                 </div>`; | ||||
|                 <div class="pf-l-flex__item"> | ||||
|                     <h3>${gettext("Context")}</h3> | ||||
|                     <code>${JSON.stringify(this.event?.context, null, 4)}</code> | ||||
|                 </div> | ||||
|                 <div class="pf-l-flex__item"> | ||||
|                     <h3>${gettext("User")}</h3> | ||||
|                     <code>${JSON.stringify(this.event?.user, null, 4)}</code> | ||||
|                 </div> | ||||
|             </div>`; | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
| @ -94,7 +95,7 @@ export class EventInfo extends LitElement { | ||||
|         case "model_deleted": | ||||
|             return html` | ||||
|                 <h3>${gettext("Affected model:")}</h3> | ||||
|                 ${this.getModelInfo(this.event.context.model as EventContext)} | ||||
|                 ${this.getModelInfo(this.event.context?.model as EventContext)} | ||||
|                 `; | ||||
|         case "authorize_application": | ||||
|             return html`<div class="pf-l-flex"> | ||||
| @ -104,8 +105,8 @@ export class EventInfo extends LitElement { | ||||
|                     </div> | ||||
|                     <div class="pf-l-flex__item"> | ||||
|                         <h3>${gettext("Using flow")}</h3> | ||||
|                         <span>${until(Flow.list({ | ||||
|                             flow_uuid: this.event.context.flow as string, | ||||
|                         <span>${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ | ||||
|                             flowUuid: this.event.context.flow as string, | ||||
|                         }).then(resp => { | ||||
|                             return html`<a href="#/flow/flows/${resp.results[0].slug}">${resp.results[0].name}</a>`; | ||||
|                         }), html`<ak-spinner size=${SpinnerSize.Medium}></ak-spinner>`)} | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| import { gettext } from "django"; | ||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { Event } from "../../api/Events"; | ||||
| import { EventsApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { EventWithContext } from "../../api/Events"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import "./EventInfo"; | ||||
|  | ||||
| @ -13,11 +15,15 @@ export class EventInfoPage extends LitElement { | ||||
|  | ||||
|     @property() | ||||
|     set eventID(value: string) { | ||||
|         Event.get(value).then((e) => (this.event = e)); | ||||
|         new EventsApi(DEFAULT_CONFIG).eventsEventsRead({ | ||||
|             eventUuid: value | ||||
|         }).then((ev) => { | ||||
|             this.event = ev as EventWithContext; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @property({ attribute: false }) | ||||
|     event?: Event; | ||||
|     event!: EventWithContext; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES.concat(css` | ||||
|  | ||||
| @ -1,11 +1,12 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { Event, EventsApi } from "../../api"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { Event } from "../../api/Events"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { EventWithContext } from "../../api/Events"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
| import { time } from "../../utils"; | ||||
| import "./EventInfo"; | ||||
|  | ||||
| @customElement("ak-event-list") | ||||
| @ -29,10 +30,10 @@ export class EventListPage extends TablePage<Event> { | ||||
|     order = "-created"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Event>> { | ||||
|         return Event.list({ | ||||
|         return new EventsApi(DEFAULT_CONFIG).eventsEventsList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE * 3, | ||||
|             pageSize: PAGE_SIZE * 3, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -45,16 +46,16 @@ export class EventListPage extends TablePage<Event> { | ||||
|             new TableColumn("Client IP", "client_ip"), | ||||
|         ]; | ||||
|     } | ||||
|     row(item: Event): TemplateResult[] { | ||||
|     row(item: EventWithContext): TemplateResult[] { | ||||
|         return [ | ||||
|             html`<div>${item.action}</div> | ||||
|             <small>${item.app}</small>`, | ||||
|             html`<div>${item.user.username}</div> | ||||
|             html`<div>${item.user?.username}</div> | ||||
|             ${item.user.on_behalf_of ? html`<small> | ||||
|                 ${gettext(`On behalf of ${item.user.on_behalf_of.username}`)} | ||||
|             </small>` : html``}`, | ||||
|             html`<span>${time(item.created).toLocaleString()}</span>`, | ||||
|             html`<span>${item.client_ip}</span>`, | ||||
|             html`<span>${item.created?.toLocaleString()}</span>`, | ||||
|             html`<span>${item.clientIp}</span>`, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @ -62,7 +63,7 @@ export class EventListPage extends TablePage<Event> { | ||||
|         return html` | ||||
|         <td role="cell" colspan="4"> | ||||
|             <div class="pf-c-table__expandable-row-content"> | ||||
|                 <ak-event-info .event=${item}></ak-event-info> | ||||
|                 <ak-event-info .event=${item as EventWithContext}></ak-event-info> | ||||
|             </div> | ||||
|         </td> | ||||
|         <td></td> | ||||
|  | ||||
| @ -7,11 +7,13 @@ import "../../elements/policies/BoundPoliciesList"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { Rule } from "../../api/EventRules"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { EventsApi, NotificationRule } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-event-rule-list") | ||||
| export class RuleListPage extends TablePage<Rule> { | ||||
| export class RuleListPage extends TablePage<NotificationRule> { | ||||
|     expandable = true; | ||||
|  | ||||
|     searchEnabled(): boolean { | ||||
| @ -30,11 +32,11 @@ export class RuleListPage extends TablePage<Rule> { | ||||
|     @property() | ||||
|     order = "name"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Rule>> { | ||||
|         return Rule.list({ | ||||
|     apiEndpoint(page: number): Promise<AKResponse<NotificationRule>> { | ||||
|         return new EventsApi(DEFAULT_CONFIG).eventsRulesList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -48,19 +50,19 @@ export class RuleListPage extends TablePage<Rule> { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     row(item: Rule): TemplateResult[] { | ||||
|     row(item: NotificationRule): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`${item.severity}`, | ||||
|             html`${item.group?.name || gettext("None (rule disabled)")}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Rule.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.eventRules(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Rule.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.eventRules(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -72,7 +74,7 @@ export class RuleListPage extends TablePage<Rule> { | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${Rule.adminUrl("create/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.eventRules("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
| @ -82,7 +84,7 @@ export class RuleListPage extends TablePage<Rule> { | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     renderExpanded(item: Rule): TemplateResult { | ||||
|     renderExpanded(item: NotificationRule): TemplateResult { | ||||
|         return html` | ||||
|         <td role="cell" colspan="4"> | ||||
|             <div class="pf-c-table__expandable-row-content"> | ||||
|  | ||||
| @ -1,17 +1,19 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { DefaultClient, AKResponse } from "../../api/Client"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| import "../../elements/buttons/ActionButton"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { Transport } from "../../api/EventTransports"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { EventsApi, NotificationTransport } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-event-transport-list") | ||||
| export class TransportListPage extends TablePage<Transport> { | ||||
| export class TransportListPage extends TablePage<NotificationTransport> { | ||||
|     searchEnabled(): boolean { | ||||
|         return true; | ||||
|     } | ||||
| @ -28,11 +30,11 @@ export class TransportListPage extends TablePage<Transport> { | ||||
|     @property() | ||||
|     order = "name"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Transport>> { | ||||
|         return Transport.list({ | ||||
|     apiEndpoint(page: number): Promise<AKResponse<NotificationTransport>> { | ||||
|         return new EventsApi(DEFAULT_CONFIG).eventsTransportsList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -45,21 +47,26 @@ export class TransportListPage extends TablePage<Transport> { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     row(item: Transport): TemplateResult[] { | ||||
|     row(item: NotificationTransport): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`${item.mode_verbose}`, | ||||
|             html`${item.modeVerbose}`, | ||||
|             html` | ||||
|             <ak-action-button url="${DefaultClient.makeUrl(["events", "transports", item.pk,  "test"])}"> | ||||
|             <ak-action-button | ||||
|                 .apiRequest=${() => { | ||||
|                     return new EventsApi(DEFAULT_CONFIG).eventsTransportsTest({ | ||||
|                         uuid: item.pk || "", | ||||
|                     }); | ||||
|                 }}> | ||||
|                 ${gettext("Test")} | ||||
|             </ak-action-button> | ||||
|             <ak-modal-button href="${Transport.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.eventTransports(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Transport.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.eventTransports(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -71,7 +78,7 @@ export class TransportListPage extends TablePage<Transport> { | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${Transport.adminUrl("create/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.eventTransports("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -4,14 +4,15 @@ import { AKResponse } from "../../api/Client"; | ||||
| import { Table, TableColumn } from "../../elements/table/Table"; | ||||
|  | ||||
| import "../../elements/Tabs"; | ||||
| import "../../elements/AdminLoginsChart"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import "../../elements/buttons/Dropdown"; | ||||
| import "../../elements/policies/BoundPoliciesList"; | ||||
| import { FlowStageBinding, Stage } from "../../api/Flows"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { FlowsApi, FlowStageBinding, StagesApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-bound-stages-list") | ||||
| export class BoundStagesList extends Table<FlowStageBinding> { | ||||
| @ -21,11 +22,11 @@ export class BoundStagesList extends Table<FlowStageBinding> { | ||||
|     target?: string; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<FlowStageBinding>> { | ||||
|         return FlowStageBinding.list({ | ||||
|         return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({ | ||||
|             target: this.target || "", | ||||
|             ordering: "order", | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @ -41,22 +42,22 @@ export class BoundStagesList extends Table<FlowStageBinding> { | ||||
|     row(item: FlowStageBinding): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.order}`, | ||||
|             html`${item.stage_obj.name}`, | ||||
|             html`${item.stage_obj.verbose_name}`, | ||||
|             html`${item.stageObj?.name}`, | ||||
|             html`${item.stageObj?.verboseName}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${FlowStageBinding.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.stageBindings(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit Binding")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Stage.adminUrl(`${item.stage}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.stages(`${item.stage}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit Stage")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${FlowStageBinding.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.stages(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -73,7 +74,7 @@ export class BoundStagesList extends Table<FlowStageBinding> { | ||||
|             <div class="pf-c-table__expandable-row-content"> | ||||
|                 <div class="pf-c-content"> | ||||
|                     <p>${gettext("These policies control when this stage will be applied to the flow.")}</p> | ||||
|                     <ak-bound-policies-list .target=${item.policybindingmodel_ptr_id}> | ||||
|                     <ak-bound-policies-list .target=${item.policybindingmodelPtrId}> | ||||
|                     </ak-bound-policies-list> | ||||
|                 </div> | ||||
|             </div> | ||||
| @ -88,7 +89,7 @@ export class BoundStagesList extends Table<FlowStageBinding> { | ||||
|                 ${gettext("No stages are currently bound to this flow.")} | ||||
|             </div> | ||||
|             <div slot="primary"> | ||||
|                 <ak-modal-button href="${FlowStageBinding.adminUrl(`create/?target=${this.target}`)}"> | ||||
|                 <ak-modal-button href="${AdminURLManager.stageBindings(`create/?target=${this.target}`)}"> | ||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                         ${gettext("Bind Stage")} | ||||
|                     </ak-spinner-button> | ||||
| @ -106,7 +107,7 @@ export class BoundStagesList extends Table<FlowStageBinding> { | ||||
|                 <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> | ||||
|             </button> | ||||
|             <ul class="pf-c-dropdown__menu" hidden> | ||||
|                 ${until(Stage.getTypes().then((types) => { | ||||
|                 ${until(new StagesApi(DEFAULT_CONFIG).stagesAllTypes({}).then((types) => { | ||||
|                     return types.map((type) => { | ||||
|                         return html`<li> | ||||
|                             <ak-modal-button href="${type.link}"> | ||||
| @ -120,7 +121,7 @@ export class BoundStagesList extends Table<FlowStageBinding> { | ||||
|                 }), html`<ak-spinner></ak-spinner>`)} | ||||
|             </ul> | ||||
|         </ak-dropdown> | ||||
|         <ak-modal-button href="${FlowStageBinding.adminUrl(`create/?target=${this.target}`)}"> | ||||
|         <ak-modal-button href="${AdminURLManager.stageBindings(`create/?target=${this.target}`)}"> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Bind Stage")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| import { customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import FlowChart from "flowchart.js"; | ||||
| import { Flow } from "../../api/Flows"; | ||||
| import { loading } from "../../utils"; | ||||
| import { FlowsApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| export const FONT_COLOUR_DARK_MODE = "#fafafa"; | ||||
| export const FONT_COLOUR_LIGHT_MODE = "#151515"; | ||||
| @ -16,8 +17,10 @@ export class FlowDiagram extends LitElement { | ||||
|     @property() | ||||
|     set flowSlug(value: string) { | ||||
|         this._flowSlug = value; | ||||
|         Flow.diagram(value).then((data) => { | ||||
|             this.diagram = FlowChart.parse(data.diagram); | ||||
|         new FlowsApi(DEFAULT_CONFIG).flowsInstancesDiagram({ | ||||
|             slug: value, | ||||
|         }).then((data) => { | ||||
|             this.diagram = FlowChart.parse(data.diagram || ""); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { Flow } from "../../api/Flows"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| @ -8,6 +7,9 @@ import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { Flow, FlowsApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-flow-list") | ||||
| export class FlowListPage extends TablePage<Flow> { | ||||
| @ -28,10 +30,10 @@ export class FlowListPage extends TablePage<Flow> { | ||||
|     order = "slug"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Flow>> { | ||||
|         return Flow.list({ | ||||
|         return new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -54,25 +56,25 @@ export class FlowListPage extends TablePage<Flow> { | ||||
|             </a>`, | ||||
|             html`${item.name}`, | ||||
|             html`${item.designation}`, | ||||
|             html`${item.stages.length}`, | ||||
|             html`${item.policies.length}`, | ||||
|             html`${item.stages?.size}`, | ||||
|             html`${item.policies?.size}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Flow.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.flows(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Flow.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.flows(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <a class="pf-c-button pf-m-secondary ak-root-link" href="${Flow.adminUrl(`${item.pk}/execute/?next=/%23${window.location.href}`)}"> | ||||
|             <a class="pf-c-button pf-m-secondary ak-root-link" href="${AdminURLManager.flows(`${item.pk}/execute/?next=/%23${window.location.href}`)}"> | ||||
|                 ${gettext("Execute")} | ||||
|             </a> | ||||
|             <a class="pf-c-button pf-m-secondary ak-root-link" href="${Flow.adminUrl(`${item.pk}/export/`)}"> | ||||
|             <a class="pf-c-button pf-m-secondary ak-root-link" href="${AdminURLManager.flows(`${item.pk}/export/`)}"> | ||||
|                 ${gettext("Export")} | ||||
|             </a> | ||||
|             `, | ||||
| @ -81,13 +83,13 @@ export class FlowListPage extends TablePage<Flow> { | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${Flow.adminUrl("create/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.flows("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
|             <div slot="modal"></div> | ||||
|         </ak-modal-button> | ||||
|         <ak-modal-button href=${Flow.adminUrl("import/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.flows("import/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                 ${gettext("Import")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -1,25 +1,29 @@ | ||||
| import { gettext } from "django"; | ||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import { Flow } from "../../api/Flows"; | ||||
|  | ||||
| import "../../elements/Tabs"; | ||||
| import "../../elements/AdminLoginsChart"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import "../../elements/policies/BoundPoliciesList"; | ||||
| import "./BoundStagesList"; | ||||
| import "./FlowDiagram"; | ||||
| import { Flow, FlowsApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
|  | ||||
| @customElement("ak-flow-view") | ||||
| export class FlowViewPage extends LitElement { | ||||
|     @property() | ||||
|     set flowSlug(value: string) { | ||||
|         Flow.get(value).then((flow) => (this.flow = flow)); | ||||
|         new FlowsApi(DEFAULT_CONFIG).flowsInstancesRead({ | ||||
|             slug: value | ||||
|         }).then((flow) => { | ||||
|             this.flow = flow; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     flow?: Flow; | ||||
|     flow!: Flow; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES.concat( | ||||
| @ -67,7 +71,7 @@ export class FlowViewPage extends LitElement { | ||||
|                                 ${gettext("These policies control which users can access this flow.")} | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <ak-bound-policies-list .target=${this.flow.policybindingmodel_ptr_id}> | ||||
|                         <ak-bound-policies-list .target=${this.flow.policybindingmodelPtrId}> | ||||
|                         </ak-bound-policies-list> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | ||||
| @ -6,8 +6,10 @@ import { TablePage } from "../../elements/table/TablePage"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { Group } from "../../api/Groups"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { CoreApi, Group } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-group-list") | ||||
| export class GroupListPage extends TablePage<Group> { | ||||
| @ -28,10 +30,10 @@ export class GroupListPage extends TablePage<Group> { | ||||
|     order = "slug"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Group>> { | ||||
|         return Group.list({ | ||||
|         return new CoreApi(DEFAULT_CONFIG).coreGroupsList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -50,16 +52,16 @@ export class GroupListPage extends TablePage<Group> { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`${item.parent || "-"}`, | ||||
|             html`${item.users.length}`, | ||||
|             html`${item.is_superuser ? "Yes" : "No"}`, | ||||
|             html`${item.users.keys.length}`, | ||||
|             html`${item.isSuperuser ? "Yes" : "No"}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Group.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.groups(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Group.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.groups(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -70,7 +72,7 @@ export class GroupListPage extends TablePage<Group> { | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${Group.adminUrl("create/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.groups("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -1,8 +1,10 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { Outpost } from "../../api/Outposts"; | ||||
| import { OutpostsApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import "../../elements/Spinner"; | ||||
|  | ||||
| @customElement("ak-outpost-health") | ||||
| export class OutpostHealth extends LitElement { | ||||
| @ -18,7 +20,9 @@ export class OutpostHealth extends LitElement { | ||||
|         if (!this.outpostId) { | ||||
|             return html`<ak-spinner></ak-spinner>`; | ||||
|         } | ||||
|         return html`<ul>${until(Outpost.health(this.outpostId).then((oh) => { | ||||
|         return html`<ul>${until(new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsHealth({ | ||||
|             uuid: this.outpostId | ||||
|         }).then((oh) => { | ||||
|             if (oh.length === 0) { | ||||
|                 return html`<li> | ||||
|                     <ul> | ||||
| @ -32,12 +36,12 @@ export class OutpostHealth extends LitElement { | ||||
|                 return html`<li> | ||||
|                     <ul> | ||||
|                         <li role="cell"> | ||||
|                             <i class="fas fa-check pf-m-success"></i>${gettext(`Last seen: ${new Date(h.last_seen * 1000).toLocaleTimeString()}`)} | ||||
|                             <i class="fas fa-check pf-m-success"></i>${gettext(`Last seen: ${h.lastSeen?.toLocaleTimeString()}`)} | ||||
|                         </li> | ||||
|                         <li role="cell"> | ||||
|                             ${h.version_outdated ? | ||||
|                             ${h.versionOutdated ? | ||||
|                             html`<i class="fas fa-times pf-m-danger"></i> | ||||
|                                 ${gettext(`${h.version}, should be ${h.version_should}`)}` : | ||||
|                                 ${gettext(`${h.version}, should be ${h.versionShould}`)}` : | ||||
|                             html`<i class="fas fa-check pf-m-success"></i>${gettext(`Version: ${h.version}`)}`} | ||||
|                         </li> | ||||
|                     </ul> | ||||
|  | ||||
| @ -2,7 +2,6 @@ import { gettext } from "django"; | ||||
| import { customElement, property } from "lit-element"; | ||||
| import { html, TemplateResult } from "lit-html"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { Outpost } from "../../api/Outposts"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| @ -11,6 +10,10 @@ import "../../elements/buttons/SpinnerButton"; | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/TokenCopyButton"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { Outpost, OutpostsApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
| import { ifDefined } from "lit-html/directives/if-defined"; | ||||
|  | ||||
| @customElement("ak-outpost-list") | ||||
| export class OutpostListPage extends TablePage<Outpost> { | ||||
| @ -27,10 +30,10 @@ export class OutpostListPage extends TablePage<Outpost> { | ||||
|         return true; | ||||
|     } | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Outpost>> { | ||||
|         return Outpost.list({ | ||||
|         return new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -49,18 +52,18 @@ export class OutpostListPage extends TablePage<Outpost> { | ||||
|     row(item: Outpost): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`<ul>${item.providers_obj.map((p) => { | ||||
|             html`<ul>${item.providersObj?.map((p) => { | ||||
|                 return html`<li><a href="#/core/providers/${p.pk}">${p.name}</a></li>`; | ||||
|             })}</ul>`, | ||||
|             html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`, | ||||
|             html`<ak-outpost-health outpostId=${ifDefined(item.pk)}></ak-outpost-health>`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.outposts(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button>  | ||||
|             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.outposts(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -88,7 +91,7 @@ export class OutpostListPage extends TablePage<Outpost> { | ||||
|                                     <span class="pf-c-form__label-text">AUTHENTIK_TOKEN</span> | ||||
|                                 </label> | ||||
|                                 <div> | ||||
|                                     <ak-token-copy-button identifier="${item.token_identifier}"> | ||||
|                                     <ak-token-copy-button identifier="${ifDefined(item.tokenIdentifier)}"> | ||||
|                                         ${gettext("Click to copy token")} | ||||
|                                     </ak-token-copy-button> | ||||
|                                 </div> | ||||
| @ -112,7 +115,7 @@ export class OutpostListPage extends TablePage<Outpost> { | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html` | ||||
|         <ak-modal-button href=${Outpost.adminUrl("create/")}> | ||||
|         <ak-modal-button href=${AdminURLManager.outposts("create/")}> | ||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                 ${gettext("Create")} | ||||
|             </ak-spinner-button> | ||||
|  | ||||
| @ -2,7 +2,6 @@ import { gettext } from "django"; | ||||
| import { customElement, property } from "lit-element"; | ||||
| import { html, TemplateResult } from "lit-html"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { OutpostServiceConnection } from "../../api/Outposts"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| @ -12,9 +11,12 @@ import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/Dropdown"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { OutpostsApi, ServiceConnection } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-outpost-service-connection-list") | ||||
| export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceConnection> { | ||||
| export class OutpostServiceConnectionListPage extends TablePage<ServiceConnection> { | ||||
|     pageTitle(): string { | ||||
|         return "Outpost Service-Connections"; | ||||
|     } | ||||
| @ -28,14 +30,15 @@ export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceCo | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<OutpostServiceConnection>> { | ||||
|         return OutpostServiceConnection.list({ | ||||
|     apiEndpoint(page: number): Promise<AKResponse<ServiceConnection>> { | ||||
|         return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     columns(): TableColumn[] { | ||||
|         return [ | ||||
|             new TableColumn("Name", "name"), | ||||
| @ -49,25 +52,28 @@ export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceCo | ||||
|     @property() | ||||
|     order = "name"; | ||||
|  | ||||
|     row(item: OutpostServiceConnection): TemplateResult[] { | ||||
|     row(item: ServiceConnection): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`${item.verbose_name}`, | ||||
|             html`${item.verboseName}`, | ||||
|             html`${item.local ? "Yes" : "No"}`, | ||||
|             html`${until(OutpostServiceConnection.state(item.pk).then((state) => { | ||||
|                 if (state.healthy) { | ||||
|                     return html`<i class="fas fa-check pf-m-success"></i> ${state.version}`; | ||||
|                 } | ||||
|                 return html`<i class="fas fa-times pf-m-danger"></i> ${gettext("Unhealthy")}`; | ||||
|             }), html`<ak-spinner></ak-spinner>`)}`, | ||||
|             html`${until( | ||||
|                 new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllState({ | ||||
|                     uuid: item.pk || "" | ||||
|                 }).then((state) => { | ||||
|                     if (state.healthy) { | ||||
|                         return html`<i class="fas fa-check pf-m-success"></i> ${state.version}`; | ||||
|                     } | ||||
|                     return html`<i class="fas fa-times pf-m-danger"></i> ${gettext("Unhealthy")}`; | ||||
|                 }), html`<ak-spinner></ak-spinner>`)}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${OutpostServiceConnection.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.outpostServiceConnections(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${OutpostServiceConnection.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.outpostServiceConnections(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -84,7 +90,7 @@ export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceCo | ||||
|                 <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> | ||||
|             </button> | ||||
|             <ul class="pf-c-dropdown__menu" hidden> | ||||
|                 ${until(OutpostServiceConnection.getTypes().then((types) => { | ||||
|                 ${until(new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllTypes({}).then((types) => { | ||||
|                     return types.map((type) => { | ||||
|                         return html`<li> | ||||
|                             <ak-modal-button href="${type.link}"> | ||||
|  | ||||
| @ -7,9 +7,11 @@ import "../../elements/buttons/ModalButton"; | ||||
| import "../../elements/buttons/Dropdown"; | ||||
| import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { Policy } from "../../api/Policies"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { PoliciesApi, Policy } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-policy-list") | ||||
| export class PolicyListPage extends TablePage<Policy> { | ||||
| @ -30,10 +32,10 @@ export class PolicyListPage extends TablePage<Policy> { | ||||
|     order = "name"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Policy>> { | ||||
|         return Policy.list({ | ||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesAllList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -50,29 +52,29 @@ export class PolicyListPage extends TablePage<Policy> { | ||||
|         return [ | ||||
|             html`<div> | ||||
|                 <div>${item.name}</div> | ||||
|                 ${item.bound_to > 0 ? | ||||
|                 ${(item.boundTo || 0) > 0 ? | ||||
|                         html`<i class="pf-icon pf-icon-ok"></i> | ||||
|                         <small> | ||||
|                             ${gettext(`Assigned to ${item.bound_to} objects.`)} | ||||
|                             ${gettext(`Assigned to ${item.boundTo} objects.`)} | ||||
|                         </small>`: | ||||
|                     html`<i class="pf-icon pf-icon-warning-triangle"></i> | ||||
|                     <small>${gettext("Warning: Policy is not assigned.")}</small>`} | ||||
|             </div>`, | ||||
|             html`${item.verbose_name}`, | ||||
|             html`${item.verboseName}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Policy.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.policies(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Policy.adminUrl(`${item.pk}/test/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.policies(`${item.pk}/test/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Test")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Policy.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.policies(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -90,7 +92,7 @@ export class PolicyListPage extends TablePage<Policy> { | ||||
|                     <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> | ||||
|                 </button> | ||||
|                 <ul class="pf-c-dropdown__menu" hidden> | ||||
|                     ${until(Policy.getTypes().then((types) => { | ||||
|                     ${until(new PoliciesApi(DEFAULT_CONFIG).policiesAllTypes({}).then((types) => { | ||||
|                         return types.map((type) => { | ||||
|                             return html`<li> | ||||
|                                 <ak-modal-button href="${type.link}"> | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { PropertyMapping } from "../../api/PropertyMapping"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| @ -10,6 +9,9 @@ import "../../elements/buttons/SpinnerButton"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { PropertyMapping, PropertymappingsApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-property-mapping-list") | ||||
| export class PropertyMappingListPage extends TablePage<PropertyMapping> { | ||||
| @ -33,12 +35,12 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> { | ||||
|     hideManaged = false; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<PropertyMapping>> { | ||||
|         return PropertyMapping.list({ | ||||
|         return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|             managed__isnull: this.hideManaged, | ||||
|             managedIsnull: this.hideManaged.toString(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @ -53,21 +55,21 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> { | ||||
|     row(item: PropertyMapping): TemplateResult[] { | ||||
|         return [ | ||||
|             html`${item.name}`, | ||||
|             html`${item.verbose_name}`, | ||||
|             html`${item.verboseName}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${PropertyMapping.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.propertyMappings(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${PropertyMapping.adminUrl(`${item.pk}/test/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.propertyMappings(`${item.pk}/test/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Test")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${PropertyMapping.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.propertyMappings(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -85,7 +87,7 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> { | ||||
|                 <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> | ||||
|             </button> | ||||
|             <ul class="pf-c-dropdown__menu" hidden> | ||||
|                 ${until(PropertyMapping.getTypes().then((types) => { | ||||
|                 ${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllTypes({}).then((types) => { | ||||
|                     return types.map((type) => { | ||||
|                         return html`<li> | ||||
|                             <ak-modal-button href="${type.link}"> | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| import { gettext } from "django"; | ||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { Provider } from "../../api/Providers"; | ||||
| import { OAuth2Provider, OAuth2SetupURLs } from "../../api/providers/OAuth2"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| import "../../elements/buttons/ModalButton"; | ||||
| @ -11,6 +9,9 @@ import "../../elements/Tabs"; | ||||
| import { Page } from "../../elements/Page"; | ||||
| import { convertToTitle } from "../../utils"; | ||||
| import "./RelatedApplicationButton"; | ||||
| import { OAuth2Provider, OAuth2ProviderSetupURLs, ProvidersApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-provider-oauth2-view") | ||||
| export class OAuth2ProviderViewPage extends Page { | ||||
| @ -26,15 +27,24 @@ export class OAuth2ProviderViewPage extends Page { | ||||
|  | ||||
|     @property({type: Number}) | ||||
|     set providerID(value: number) { | ||||
|         OAuth2Provider.get(value).then((app) => this.provider = app); | ||||
|         OAuth2Provider.getLaunchURls(value).then((urls) => this.providerUrls = urls); | ||||
|         const api = new ProvidersApi(DEFAULT_CONFIG); | ||||
|         api.providersOauth2Read({ | ||||
|             id: value | ||||
|         }).then((prov) => { | ||||
|             this.provider = prov; | ||||
|         }); | ||||
|         api.providersOauth2SetupUrls({ | ||||
|             id: value | ||||
|         }).then((prov) => { | ||||
|             this.providerUrls = prov; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @property({ attribute: false }) | ||||
|     provider?: OAuth2Provider; | ||||
|  | ||||
|     @property({ attribute: false }) | ||||
|     providerUrls?: OAuth2SetupURLs; | ||||
|     providerUrls?: OAuth2ProviderSetupURLs; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES; | ||||
| @ -82,7 +92,7 @@ export class OAuth2ProviderViewPage extends Page { | ||||
|                                                 <span class="pf-c-description-list__text">${gettext("Client type")}</span> | ||||
|                                             </dt> | ||||
|                                             <dd class="pf-c-description-list__description"> | ||||
|                                                 <div class="pf-c-description-list__text">${convertToTitle(this.provider.client_type)}</div> | ||||
|                                                 <div class="pf-c-description-list__text">${convertToTitle(this.provider.clientType || "")}</div> | ||||
|                                             </dd> | ||||
|                                         </div> | ||||
|                                         <div class="pf-c-description-list__group"> | ||||
| @ -90,7 +100,7 @@ export class OAuth2ProviderViewPage extends Page { | ||||
|                                                 <span class="pf-c-description-list__text">${gettext("Client ID")}</span> | ||||
|                                             </dt> | ||||
|                                             <dd class="pf-c-description-list__description"> | ||||
|                                                 <div class="pf-c-description-list__text">${this.provider.client_id}</div> | ||||
|                                                 <div class="pf-c-description-list__text">${this.provider.clientId}</div> | ||||
|                                             </dd> | ||||
|                                         </div> | ||||
|                                         <div class="pf-c-description-list__group"> | ||||
| @ -98,13 +108,13 @@ export class OAuth2ProviderViewPage extends Page { | ||||
|                                                 <span class="pf-c-description-list__text">${gettext("Redirect URIs")}</span> | ||||
|                                             </dt> | ||||
|                                             <dd class="pf-c-description-list__description"> | ||||
|                                                 <div class="pf-c-description-list__text">${this.provider.redirect_uris}</div> | ||||
|                                                 <div class="pf-c-description-list__text">${this.provider.redirectUris}</div> | ||||
|                                             </dd> | ||||
|                                         </div> | ||||
|                                     </dl> | ||||
|                                 </div> | ||||
|                                 <div class="pf-c-card__footer"> | ||||
|                                     <ak-modal-button href="${Provider.adminUrl(`${this.provider.pk}/update/`)}"> | ||||
|                                     <ak-modal-button href="${AdminURLManager.providers(`${this.provider.pk}/update/`)}"> | ||||
|                                         <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||
|                                             ${gettext("Edit")} | ||||
|                                         </ak-spinner-button> | ||||
| @ -125,7 +135,7 @@ export class OAuth2ProviderViewPage extends Page { | ||||
|                                             <label class="pf-c-form__label" for="help-text-simple-form-name"> | ||||
|                                                 <span class="pf-c-form__label-text">${gettext("OpenID Configuration URL")}</span> | ||||
|                                             </label> | ||||
|                                             <input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.provider_info || "-"}" /> | ||||
|                                             <input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.providerInfo || "-"}" /> | ||||
|                                         </div> | ||||
|                                         <div class="pf-c-form__group"> | ||||
|                                             <label class="pf-c-form__label" for="help-text-simple-form-name"> | ||||
| @ -150,7 +160,7 @@ export class OAuth2ProviderViewPage extends Page { | ||||
|                                             <label class="pf-c-form__label" for="help-text-simple-form-name"> | ||||
|                                                 <span class="pf-c-form__label-text">${gettext("Userinfo URL")}</span> | ||||
|                                             </label> | ||||
|                                             <input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.user_info || "-"}" /> | ||||
|                                             <input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.userInfo || "-"}" /> | ||||
|                                         </div> | ||||
|                                         <div class="pf-c-form__group"> | ||||
|                                             <label class="pf-c-form__label" for="help-text-simple-form-name"> | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| import { gettext } from "django"; | ||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | ||||
| import { Provider } from "../../api/Providers"; | ||||
| import { AKResponse } from "../../api/Client"; | ||||
| import { TablePage } from "../../elements/table/TablePage"; | ||||
|  | ||||
| @ -10,6 +9,9 @@ import "../../elements/buttons/Dropdown"; | ||||
| import { TableColumn } from "../../elements/table/Table"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { PAGE_SIZE } from "../../constants"; | ||||
| import { Provider, ProvidersApi } from "../../api"; | ||||
| import { DEFAULT_CONFIG } from "../../api/Config"; | ||||
| import { AdminURLManager } from "../../api/legacy"; | ||||
|  | ||||
| @customElement("ak-provider-list") | ||||
| export class ProviderListPage extends TablePage<Provider> { | ||||
| @ -30,10 +32,10 @@ export class ProviderListPage extends TablePage<Provider> { | ||||
|     order = "name"; | ||||
|  | ||||
|     apiEndpoint(page: number): Promise<AKResponse<Provider>> { | ||||
|         return Provider.list({ | ||||
|         return new ProvidersApi(DEFAULT_CONFIG).providersAllList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             page_size: PAGE_SIZE, | ||||
|             pageSize: PAGE_SIZE, | ||||
|             search: this.search || "", | ||||
|         }); | ||||
|     } | ||||
| @ -52,21 +54,21 @@ export class ProviderListPage extends TablePage<Provider> { | ||||
|             html`<a href="#/core/providers/${item.pk}"> | ||||
|                 ${item.name} | ||||
|             </a>`, | ||||
|             item.assigned_application_name ? | ||||
|             item.assignedApplicationName ? | ||||
|                 html`<i class="pf-icon pf-icon-ok"></i> | ||||
|                     ${gettext("Assigned to application ")} | ||||
|                     <a href="#/core/applications/${item.assigned_application_slug}">${item.assigned_application_name}</a>` : | ||||
|                     <a href="#/core/applications/${item.assignedApplicationSlug}">${item.assignedApplicationName}</a>` : | ||||
|                 html`<i class="pf-icon pf-icon-warning-triangle"></i> | ||||
|                 ${gettext("Warning: Provider not assigned to any application.")}`, | ||||
|             html`${item.verbose_name}`, | ||||
|             html`${item.verboseName}`, | ||||
|             html` | ||||
|             <ak-modal-button href="${Provider.adminUrl(`${item.pk}/update/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.providers(`${item.pk}/update/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||
|                     ${gettext("Edit")} | ||||
|                 </ak-spinner-button> | ||||
|                 <div slot="modal"></div> | ||||
|             </ak-modal-button> | ||||
|             <ak-modal-button href="${Provider.adminUrl(`${item.pk}/delete/`)}"> | ||||
|             <ak-modal-button href="${AdminURLManager.providers(`${item.pk}/delete/`)}"> | ||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||
|                     ${gettext("Delete")} | ||||
|                 </ak-spinner-button> | ||||
| @ -84,7 +86,7 @@ export class ProviderListPage extends TablePage<Provider> { | ||||
|                 <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> | ||||
|             </button> | ||||
|             <ul class="pf-c-dropdown__menu" hidden> | ||||
|                 ${until(Provider.getTypes().then((types) => { | ||||
|                 ${until(new ProvidersApi(DEFAULT_CONFIG).providersAllTypes({}).then((types) => { | ||||
|                     return types.map((type) => { | ||||
|                         return html`<li> | ||||
|                             <ak-modal-button href="${type.link}"> | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L