diff --git a/README.md b/README.md index ccde18e..d5a3f9b 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Additionally, you can use the following options when starting an instance: - `ldap`: set up an LDAP server. - `saml`: set up authentik as a SAML IDP. - `oidc`: set up authentik as an OIDC IDP. +- `scim`: set up authentik as a SCIM server. - `office`: set up a Nextcloud Office server. - `onlyoffice` setup an onlyoffice document server. - `push` set up [client push](https://github.com/nextcloud/notify_push). diff --git a/blueprints/authentik-scim.yaml b/blueprints/authentik-scim.yaml new file mode 100644 index 0000000..8b81ef7 --- /dev/null +++ b/blueprints/authentik-scim.yaml @@ -0,0 +1,55 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: haze-nextcloud-scim +entries: + - model: authentik_sources_scim.scimsource + id: scim-source + identifiers: + slug: nextcloud-scim + attrs: + name: Nextcloud SCIM + enabled: true + + - model: authentik_core.token + identifiers: + identifier: !Format ["ak-source-scim-%s", !KeyOf scim-source] + attrs: + key: haze-scim-token + intent: api + expiring: false + user: + !Find [ + authentik_core.user, + [username, !Format ["ak-source-scim-%s", !KeyOf scim-source]], + ] + + # Pushes Authentik's users and groups into Authentik's SCIM source + - model: authentik_providers_scim.scimprovider + id: scim-provider + identifiers: + name: Nextcloud SCIM + attrs: + url: http://authentik:9000/source/scim/nextcloud-scim/v2 + token: haze-scim-token + exclude_users_service_account: true + property_mappings: + - !Find [ + authentik_providers_scim.scimmapping, + [managed, goauthentik.io/providers/scim/user], + ] + property_mappings_group: + - !Find [ + authentik_providers_scim.scimmapping, + [managed, goauthentik.io/providers/scim/group], + ] + + - model: authentik_core.application + identifiers: + slug: nextcloud-scim + attrs: + name: Nextcloud SCIM + backchannel_providers: + - !KeyOf scim-provider + meta_launch_url: !File /haze/authentik/nextcloud-url + meta_description: Nextcloud instance provisioned by haze diff --git a/src/service.rs b/src/service.rs index d0bc08e..a70413e 100644 --- a/src/service.rs +++ b/src/service.rs @@ -19,7 +19,7 @@ mod webhook; use crate::cloud::CloudOptions; use crate::config::{HazeConfig, Preset, ProxyConfig}; -pub use crate::service::authentik::{Authentik, AuthentikOidc, AuthentikSaml}; +pub use crate::service::authentik::{Authentik, AuthentikOidc, AuthentikSaml, AuthentikScim}; pub use crate::service::clam::{Clam, ClamIcap, ClamIcapTls, ClamSocket}; use crate::service::dav::Dav; use crate::service::imaginary::Imaginary; @@ -322,6 +322,8 @@ pub enum ServiceType { Saml, /// Configure Authentik as an OIDC IDP for Nextcloud Oidc, + /// Configure Authentik as a SCIM server for Nextcloud + Scim, } #[enum_dispatch] @@ -358,6 +360,7 @@ pub enum Service { Authentik(Authentik), AuthentikSaml(AuthentikSaml), AuthentikOidc(AuthentikOidc), + AuthentikScim(AuthentikScim), Preset(PresetService), } @@ -413,6 +416,10 @@ impl Service { Service::Authentik(Authentik), Service::AuthentikOidc(AuthentikOidc), ]), + ServiceType::Scim => Some(vec![ + Service::Authentik(Authentik), + Service::AuthentikScim(AuthentikScim), + ]), } } else { presets diff --git a/src/service/authentik/mod.rs b/src/service/authentik/mod.rs index 3a72568..dd7b364 100644 --- a/src/service/authentik/mod.rs +++ b/src/service/authentik/mod.rs @@ -1,8 +1,10 @@ mod oidc; mod saml; +mod scim; pub use oidc::AuthentikOidc; pub use saml::AuthentikSaml; +pub use scim::AuthentikScim; use crate::Result; use crate::cloud::CloudOptions; diff --git a/src/service/authentik/scim.rs b/src/service/authentik/scim.rs new file mode 100644 index 0000000..f9887a6 --- /dev/null +++ b/src/service/authentik/scim.rs @@ -0,0 +1,73 @@ +use super::{AUTHENTIK_PORT, container_ip, container_name, write_file}; +use crate::Result; +use crate::cloud::CloudOptions; +use crate::config::{HazeConfig, ProxyConfig}; +use crate::service::{ServiceTrait, split_cmnd}; +use bollard::Docker; + +const BLUEPRINT: &str = include_str!("../../../blueprints/authentik-scim.yaml"); + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AuthentikScim; + +#[async_trait::async_trait] +impl ServiceTrait for AuthentikScim { + fn name(&self) -> &str { + "scim" + } + + async fn spawn( + &self, + _docker: &Docker, + cloud_id: &str, + _network: &str, + config: &HazeConfig, + _options: &CloudOptions, + ) -> Result> { + let blueprints_directory = config + .work_dir + .join(cloud_id) + .join("authentik") + .join("blueprints"); + write_file(&blueprints_directory, "scim.yaml", BLUEPRINT)?; + + Ok(Vec::new()) + } + + fn apps(&self) -> &'static [&'static str] { + &["circles"] + } + + async fn post_setup( + &self, + docker: &Docker, + cloud_id: &str, + config: &HazeConfig, + ) -> Result>> { + let authentik_container = container_name(cloud_id, "server"); + let authentik_url = config.proxy.addr_with_port( + &authentik_container, + container_ip(docker, &authentik_container, None).await?, + AUTHENTIK_PORT, + ); + + Ok(vec![ + split_cmnd("occ config:app:set --silent circles scim_enabled --type boolean --value 1"), + split_cmnd(&format!( + "occ config:app:set --silent circles scim_endpoint --value {authentik_url}/source/scim/nextcloud-scim/v2" + )), + split_cmnd("occ config:app:set --silent circles scim_token --value haze-scim-token"), + ]) + } + + async fn start_message( + &self, + _docker: &Docker, + cloud_id: &str, + _proxy: &ProxyConfig, + ) -> Result> { + Ok(Some(format!( + "Once Authentik as started, you can sync teams with 'haze {cloud_id} occ circles:scim:sync-circles'\n", + ))) + } +}