add invalidation_flow to saml importer

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer 2023-03-24 14:40:36 +01:00
parent 5dfda671eb
commit c637489b06
No known key found for this signature in database
9 changed files with 208 additions and 254 deletions

View file

@ -296,7 +296,6 @@ class Provider(SerializerModel):
), ),
related_name="provider_authentication", related_name="provider_authentication",
) )
authorization_flow = models.ForeignKey( authorization_flow = models.ForeignKey(
"authentik_flows.Flow", "authentik_flows.Flow",
# Set to cascade even though null is allowed, since most providers # Set to cascade even though null is allowed, since most providers

View file

@ -171,6 +171,9 @@ class SAMLProviderImportSerializer(PassiveSerializer):
authorization_flow = PrimaryKeyRelatedField( authorization_flow = PrimaryKeyRelatedField(
queryset=Flow.objects.filter(designation=FlowDesignation.AUTHORIZATION), queryset=Flow.objects.filter(designation=FlowDesignation.AUTHORIZATION),
) )
invalidation_flow = PrimaryKeyRelatedField(
queryset=Flow.objects.filter(designation=FlowDesignation.INVALIDATION),
)
file = FileField() file = FileField()
@ -260,7 +263,9 @@ class SAMLProviderViewSet(UsedByMixin, ModelViewSet):
try: try:
metadata = ServiceProviderMetadataParser().parse(file.read().decode()) metadata = ServiceProviderMetadataParser().parse(file.read().decode())
metadata.to_provider( metadata.to_provider(
data.validated_data["name"], data.validated_data["authorization_flow"] data.validated_data["name"],
data.validated_data["authorization_flow"],
data.validated_data["invalidation_flow"],
) )
except ValueError as exc: # pragma: no cover except ValueError as exc: # pragma: no cover
LOGGER.warning(str(exc)) LOGGER.warning(str(exc))

View file

@ -49,12 +49,13 @@ class ServiceProviderMetadata:
signing_keypair: Optional[CertificateKeyPair] = None signing_keypair: Optional[CertificateKeyPair] = None
def to_provider(self, name: str, authorization_flow: Flow) -> SAMLProvider: def to_provider(self, name: str, authorization_flow: Flow, invalidation_flow: Flow) -> SAMLProvider:
"""Create a SAMLProvider instance from the details. `name` is required, """Create a SAMLProvider instance from the details. `name` is required,
as depending on the metadata CertificateKeypairs might have to be created.""" as depending on the metadata CertificateKeypairs might have to be created."""
provider = SAMLProvider.objects.create( provider = SAMLProvider.objects.create(
name=name, name=name,
authorization_flow=authorization_flow, authorization_flow=authorization_flow,
invalidation_flow=invalidation_flow
) )
provider.issuer = self.entity_id provider.issuer = self.entity_id
provider.sp_binding = self.acs_binding provider.sp_binding = self.acs_binding

View file

@ -3831,6 +3831,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {
@ -3949,6 +3954,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {
@ -4063,6 +4073,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {
@ -4264,6 +4279,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {
@ -4469,6 +4489,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {
@ -4671,6 +4696,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {
@ -4776,6 +4806,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {
@ -4821,6 +4856,11 @@
"title": "Authorization flow", "title": "Authorization flow",
"description": "Flow used when authorizing this provider." "description": "Flow used when authorizing this provider."
}, },
"invalidation_flow": {
"type": "integer",
"title": "Invalidation flow",
"description": "Flow used ending the session from a provider."
},
"property_mappings": { "property_mappings": {
"type": "array", "type": "array",
"items": { "items": {

View file

@ -16390,15 +16390,15 @@ paths:
* `http://www.w3.org/2001/04/xmlenc#sha256` - SHA256 * `http://www.w3.org/2001/04/xmlenc#sha256` - SHA256
* `http://www.w3.org/2001/04/xmldsig-more#sha384` - SHA384 * `http://www.w3.org/2001/04/xmldsig-more#sha384` - SHA384
* `http://www.w3.org/2001/04/xmlenc#sha512` - SHA512 * `http://www.w3.org/2001/04/xmlenc#sha512` - SHA512
- in: query
name: is_backchannel
schema:
type: boolean
- in: query - in: query
name: invalidation_flow name: invalidation_flow
schema: schema:
type: string type: string
format: uuid format: uuid
- in: query
name: is_backchannel
schema:
type: boolean
- in: query - in: query
name: issuer name: issuer
schema: schema:
@ -40181,12 +40181,16 @@ components:
authorization_flow: authorization_flow:
type: string type: string
format: uuid format: uuid
invalidation_flow:
type: string
format: uuid
file: file:
type: string type: string
format: binary format: binary
required: required:
- authorization_flow - authorization_flow
- file - file
- invalidation_flow
- name - name
SAMLProviderRequest: SAMLProviderRequest:
type: object type: object

View file

@ -166,69 +166,6 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
required required
></ak-text-input> ></ak-text-input>
<ak-form-element-horizontal
name="authenticationFlow"
label=${msg("Authentication flow")}
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authentication}
.currentFlow=${provider?.authenticationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when a user access this provider and is not authenticated.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
name="authorizationFlow"
label=${msg("Authorization flow")}
?required=${true}
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${provider?.authorizationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when authorizing this provider.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Invalidation flow`}
?required=${true}
name="invalidationFlow"
>
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Flow[]> => {
const args: FlowsInstancesListRequest = {
ordering: "slug",
designation: FlowsInstancesListDesignationEnum.Invalidation,
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(args);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return flow.pk === this.instance?.invalidationFlow;
}}
>
</ak-search-select>
<p class="pf-c-form__helper-text">
${t`Flow used when authorizing this provider.`}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Protocol settings")} </span> <span slot="header"> ${msg("Protocol settings")} </span>
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">
@ -283,80 +220,49 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
</ak-form-group> </ak-form-group>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${t`Flow settings`} </span> <span slot="header"> ${msg("Flow settings")} </span>
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Authorization flow`} name="authenticationFlow"
?required=${true} label=${msg("Authentication flow")}
name="authorizationFlow"
> >
<ak-search-select <ak-flow-search
.fetchObjects=${async (query?: string): Promise<Flow[]> => { flowType=${FlowsInstancesListDesignationEnum.Authentication}
const args: FlowsInstancesListRequest = { .currentFlow=${provider?.authenticationFlow}
ordering: "slug", required
designation: FlowsInstancesListDesignationEnum.Authorization, ></ak-flow-search>
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(
args,
);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return flow.pk === this.instance?.authorizationFlow;
}}
>
</ak-search-select>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Flow used when authorizing this provider.`} ${msg(
"Flow used when a user access this provider and is not authenticated.",
)}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Invalidation flow`} name="authorizationFlow"
label=${msg("Authorization flow")}
?required=${true}
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${provider?.authorizationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when authorizing this provider.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Invalidation flow")}
?required=${true} ?required=${true}
name="invalidationFlow" name="invalidationFlow"
> >
<ak-search-select <ak-flow-search
.fetchObjects=${async (query?: string): Promise<Flow[]> => { flowType=${FlowsInstancesListDesignationEnum.Invalidation}
const args: FlowsInstancesListRequest = { .currentFlow=${provider?.invalidationFlow}
ordering: "slug", required
designation: FlowsInstancesListDesignationEnum.Invalidation, ></ak-flow-search>
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(
args,
);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return flow.pk === this.instance?.invalidationFlow;
}}
>
</ak-search-select>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Flow used when authorizing this provider.`} ${msg("Flow used when logging out of this provider.")}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>

View file

@ -258,7 +258,8 @@ export class ProxyProviderFormPage extends BaseProviderForm<ProxyProvider> {
} }
renderForm(): TemplateResult { renderForm(): TemplateResult {
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name"> return html`
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.name)}" value="${ifDefined(this.instance?.name)}"
@ -266,34 +267,6 @@ export class ProxyProviderFormPage extends BaseProviderForm<ProxyProvider> {
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Authentication flow")}
?required=${false}
name="authenticationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authentication}
.currentFlow=${this.instance?.authenticationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when a user access this provider and is not authenticated.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Authorization flow")}
?required=${true}
name="authorizationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${this.instance?.authorizationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when authorizing this provider.")}
</p>
</ak-form-element-horizontal>
<div class="pf-c-card pf-m-selectable pf-m-selected"> <div class="pf-c-card pf-m-selectable pf-m-selected">
<div class="pf-c-card__body">${this.renderModeSelector()}</div> <div class="pf-c-card__body">${this.renderModeSelector()}</div>
@ -448,6 +421,57 @@ ${this.instance?.skipPathRegex}</textarea
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>
</ak-form-group>`; </ak-form-group>
<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Flow settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Authentication flow")}
?required=${false}
name="authenticationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authentication}
.currentFlow=${this.instance?.authenticationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg(
"Flow used when a user access this provider and is not authenticated.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Authorization flow")}
?required=${true}
name="authorizationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${this.instance?.authorizationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when authorizing this provider.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Invalidation flow")}
?required=${true}
name="invalidationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Invalidation}
.currentFlow=${this.instance?.invalidationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when logging out of this provider.")}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>
`;
} }
} }

View file

@ -66,34 +66,6 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Authentication flow")}
?required=${false}
name="authenticationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authentication}
.currentFlow=${this.instance?.authenticationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when a user access this provider and is not authenticated.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Authorization flow")}
?required=${true}
name="authorizationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${this.instance?.authorizationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when authorizing this provider.")}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Protocol settings")} </span> <span slot="header"> ${msg("Protocol settings")} </span>
@ -160,80 +132,50 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
</ak-form-group> </ak-form-group>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${t`Flow settings`} </span> <span slot="header"> ${msg("Flow settings")} </span>
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Authorization flow`} label=${msg("Authentication flow")}
?required=${true} ?required=${false}
name="authorizationFlow" name="authenticationFlow"
> >
<ak-search-select <ak-flow-search
.fetchObjects=${async (query?: string): Promise<Flow[]> => { flowType=${FlowsInstancesListDesignationEnum.Authentication}
const args: FlowsInstancesListRequest = { .currentFlow=${this.instance?.authenticationFlow}
ordering: "slug", required
designation: FlowsInstancesListDesignationEnum.Authorization, ></ak-flow-search>
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(
args,
);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return flow.pk === this.instance?.authorizationFlow;
}}
>
</ak-search-select>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Flow used when authorizing this provider.`} ${msg(
"Flow used when a user access this provider and is not authenticated.",
)}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Invalidation flow`} label=${msg("Authorization flow")}
?required=${true}
name="authorizationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
.currentFlow=${this.instance?.authorizationFlow}
required
></ak-flow-search>
<p class="pf-c-form__helper-text">
${msg("Flow used when authorizing this provider.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Invalidation flow")}
?required=${true} ?required=${true}
name="invalidationFlow" name="invalidationFlow"
> >
<ak-search-select <ak-flow-search
.fetchObjects=${async (query?: string): Promise<Flow[]> => { flowType=${FlowsInstancesListDesignationEnum.Invalidation}
const args: FlowsInstancesListRequest = { .currentFlow=${this.instance?.invalidationFlow}
ordering: "slug", required
designation: FlowsInstancesListDesignationEnum.Invalidation, ></ak-flow-search>
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(
args,
);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return flow.pk === this.instance?.invalidationFlow;
}}
>
</ak-search-select>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Flow used when authorizing this provider.`} ${msg("Flow used when logging out of this provider.")}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>

View file

@ -26,6 +26,7 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
file: file, file: file,
name: data.name, name: data.name,
authorizationFlow: data.authorizationFlow || "", authorizationFlow: data.authorizationFlow || "",
invalidationFlow: data.invalidationFlow || "",
}); });
} }
@ -46,6 +47,38 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
${msg("Flow used when authorizing this provider.")} ${msg("Flow used when authorizing this provider.")}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Invalidation flow`}
?required=${true}
name="invalidationFlow"
>
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Flow[]> => {
const args: FlowsInstancesListRequest = {
ordering: "slug",
designation: FlowsInstancesListDesignationEnum.Invalidation,
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(args);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
>
</ak-search-select>
<p class="pf-c-form__helper-text">
${t`Flow used when logging out of this provider.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Metadata")} name="metadata"> <ak-form-element-horizontal label=${msg("Metadata")} name="metadata">
<input type="file" value="" class="pf-c-form-control" /> <input type="file" value="" class="pf-c-form-control" />