Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 180 additions & 68 deletions tf/deployment/data/users.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions tf/deployment/modules/scoped/discord/community/users.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@ resource "discord_member_roles" "roles" {
user_id = each.value.discord.id
role {
role_id = discord_role.admin.id
has_role = contains(["admin"], each.value.role)
has_role = contains(each.value.roles, "admin")
}

role {
role_id = discord_role.team.id
has_role = contains(["yucca", "team", "admin"], each.value.role)
has_role = length(setintersection(toset(each.value.roles), toset(["yucca", "team", "admin"]))) > 0
}

role {
role_id = discord_role.contributor.id
has_role = contains(["contributor", "futo", "yucca", "team", "admin"], each.value.role)
has_role = length(setintersection(toset(each.value.roles), toset(["contributor", "futo", "yucca", "team", "admin"]))) > 0
}

role {
role_id = discord_role.futo.id
has_role = contains(["futo", "yucca", "team", "admin"], each.value.role)
has_role = length(setintersection(toset(each.value.roles), toset(["futo", "yucca", "team", "admin"]))) > 0
}

role {
role_id = discord_role.support_crew.id
has_role = contains(["support"], each.value.role)
has_role = contains(each.value.roles, "support")
}

role {
role_id = discord_role.yucca.id
has_role = contains(["yucca"], each.value.role) || contains(try(each.value.discord.extra_roles, []), "yucca")
has_role = contains(each.value.roles, "yucca")
}
}
18 changes: 9 additions & 9 deletions tf/deployment/modules/shared/github/org/users.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ locals {

collaborators = {
for user in local.github_users : user.github.username => (
(user.role == "contributor" || user.role == "futo") ? "maintain" :
(user.role == "support" ? "triage" : null)
length(setintersection(toset(user.roles), toset(["contributor", "futo"]))) > 0 ? "maintain" :
(contains(user.roles, "support") ? "triage" : null)
)
if user.role == "contributor" || user.role == "support" || user.role == "futo"
if length(setintersection(toset(user.roles), toset(["contributor", "support", "futo"]))) > 0
}

bots = {
Expand Down Expand Up @@ -41,11 +41,11 @@ resource "github_team_members" "team" {
dynamic "members" {
for_each = {
for user in local.github_users : user.github.username => user
if user.role == "team"
if contains(user.roles, "team")
}
content {
username = members.value.github.username
role = members.value.role == "admin" ? "maintainer" : "member"
role = contains(members.value.roles, "admin") ? "maintainer" : "member"
}
}
}
Expand All @@ -55,23 +55,23 @@ resource "github_team_members" "leadership" {
dynamic "members" {
for_each = {
for user in local.github_users : user.github.username => user
if user.role == "admin"
if contains(user.roles, "admin")
}
content {
username = members.value.github.username
role = members.value.role == "admin" ? "maintainer" : "member"
role = contains(members.value.roles, "admin") ? "maintainer" : "member"
}
}
}

resource "github_membership" "org_members" {
for_each = {
for user in local.github_users : user.github.username => user
if user.role == "team" || user.role == "yucca" || user.role == "admin"
if length(setintersection(toset(user.roles), toset(["team", "yucca", "admin"]))) > 0
}

username = each.key
role = each.value.role == "admin" ? "admin" : "member"
role = contains(each.value.roles, "admin") ? "admin" : "member"
}

resource "github_repository_collaborators" "repo_collaborators" {
Expand Down
18 changes: 18 additions & 0 deletions tf/deployment/modules/shared/zitadel/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions tf/deployment/modules/shared/zitadel/actions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,29 @@ resource "zitadel_trigger_actions" "map_roles" {
trigger_type = "TRIGGER_TYPE_PRE_USERINFO_CREATION"
flow_type = "FLOW_TYPE_CUSTOMISE_TOKEN"
}

resource "zitadel_action" "saml_map_roles" {
org_id = zitadel_org.immich.id
name = "samlMapRoles"
script = <<-EOT
function samlMapRoles(ctx, api) {
if (ctx.v1.user.grants == undefined || ctx.v1.user.grants.count == 0) {
return;
}
let roles = [];
ctx.v1.user.grants.grants.forEach(grant => {
roles.push(grant.roles)
})
api.v1.attributes.setCustomAttribute('Roles', '', ...roles)
}
EOT
allowed_to_fail = false
timeout = "10s"
}

resource "zitadel_trigger_actions" "saml_map_roles" {
org_id = zitadel_org.immich.id
action_ids = [zitadel_action.saml_map_roles.id]
trigger_type = "TRIGGER_TYPE_PRE_SAML_RESPONSE_CREATION"
flow_type = "FLOW_TYPE_SAML_RESPONSE"
}
4 changes: 4 additions & 0 deletions tf/deployment/modules/shared/zitadel/config.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ terraform {
source = "1Password/onepassword"
version = "~> 2.1"
}
http = {
source = "hashicorp/http"
version = "~> 3.5"
}
}
}

4 changes: 2 additions & 2 deletions tf/deployment/modules/shared/zitadel/defaults.tf
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ resource "zitadel_project" "zitadel" {
resource "zitadel_instance_member" "superusers" {
for_each = {
for user in local.users_data : user.github.id => user
if user.github.username != null && user.github.username != "" && user.role == "admin"
if user.github.username != null && user.github.username != "" && contains(user.roles, "admin")
}
user_id = zitadel_human_user.users[each.key].id
roles = ["IAM_OWNER"]
Expand All @@ -57,7 +57,7 @@ resource "zitadel_project_role" "zitadel_admin" {
resource "zitadel_user_grant" "superusers" {
for_each = {
for user in local.users_data : user.github.id => user
if user.github.username != null && user.github.username != "" && user.role == "admin"
if user.github.username != null && user.github.username != "" && contains(user.roles, "admin")
}
org_id = zitadel_project.zitadel.org_id
project_id = zitadel_project.zitadel.id
Expand Down
23 changes: 12 additions & 11 deletions tf/deployment/modules/shared/zitadel/permissions.tf
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
locals {
project_to_displaynames = flatten([
for project in local.projects : [
for role in keys(project.roles) : {
for role in project.roles : {
project_name = project.name
role_key = role
role_key = role.key
}
]
])

project_to_zitadel_role_to_immich_role = flatten([
# For each user+project, grant only the highest-priority role (first match in the ordered list)
project_user_grants = flatten([
for project in local.projects : [
for zitadel_role, immich_roles in project.roles : [
[for user in local.users_data : {
project_name = project.name
role_key = zitadel_role
github_user_id = user.github.id
} if contains(immich_roles, user.role) && user.github.username != null && user.github.username != ""]
]
for user in local.users_data : {
project_name = project.name
role_key = [for role in project.roles : role.key if length(setintersection(toset(role.grants_to), toset(user.roles))) > 0][0]
github_user_id = user.github.id
}
if length([for role in project.roles : role.key if length(setintersection(toset(role.grants_to), toset(user.roles))) > 0]) > 0
&& user.github.username != null && user.github.username != ""
]
])
}
Expand All @@ -35,7 +36,7 @@ resource "zitadel_project_role" "project_roles" {

resource "zitadel_user_grant" "project_grants" {
for_each = {
for user_role in local.project_to_zitadel_role_to_immich_role : "${user_role.project_name}_${user_role.role_key}_${user_role.github_user_id}" => user_role
for grant in local.project_user_grants : "${grant.project_name}_${grant.github_user_id}" => grant
}
depends_on = [zitadel_project_role.project_roles]

Expand Down
54 changes: 44 additions & 10 deletions tf/deployment/modules/shared/zitadel/project.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,68 @@ locals {
appType = "WEB"
redirectUris = []
grantTypes = ["AUTHORIZATION_CODE"]
protocol = "oidc"
metadataUrl = ""
}
projects_data = [
{
name = "Grafana Monitoring Prod"
roles = { "GrafanaAdmin" : ["admin"], "Editor" : ["team"] }
roles = [{ key = "GrafanaAdmin", grants_to = ["admin"] }, { key = "Editor", grants_to = ["team"] }]
redirectUris = ["https://monitoring.immich.cloud/login/generic_oauth"]
},
{
name = "Grafana Monitoring Dev"
roles = { "GrafanaAdmin" : ["admin"], "Editor" : ["team"] }
roles = [{ key = "GrafanaAdmin", grants_to = ["admin"] }, { key = "Editor", grants_to = ["team"] }]
redirectUris = ["https://monitoring.dev.immich.cloud/login/generic_oauth"]
},
{
name = "Grafana Data Prod"
roles = { "GrafanaAdmin" : ["admin"], "Editor" : ["team"] }
roles = [{ key = "GrafanaAdmin", grants_to = ["admin"] }, { key = "Editor", grants_to = ["team"] }]
redirectUris = ["https://grafana.data.immich.cloud/login/generic_oauth"]
},
{
name = "Outline"
roles = { "Leadership" : ["admin"], "Team" : ["team"], "Contributor" : ["contributor"], "Support Crew" : ["support"] }
name = "Outline"
roles = [
{ key = "Leadership", grants_to = ["admin"] },
{ key = "Team", grants_to = ["team"] },
{ key = "Contributor", grants_to = ["contributor"] },
{ key = "Support Crew", grants_to = ["support"] }
]
authMethod = "BASIC"
redirectUris = ["https://outline.immich.cloud/auth/oidc.callback"]
},
{
name = "ContainerSSH"
roles = {
"Granted" : ["admin", "team", "contributor"]
}
roles = [
{ key = "Granted", grants_to = ["admin", "team", "contributor"] }
]
appType = "NATIVE"
grantTypes = ["DEVICE_CODE"]
},
{
name = "OAuth2 Proxy"
roles = { "Granted" : ["admin", "team"] }
roles = [{ key = "Granted", grants_to = ["admin", "team"] }]
redirectUris = ["https://oauth2-proxy.internal.immich.cloud/oauth2/callback"]
},
{
name = "OVHCloud"
protocol = "saml"
roles = [{ key = "ADMIN", grants_to = ["admin", "yucca"] }, { key = "DEFAULT", grants_to = ["team"] }]
metadataUrl = "https://auth.eu.ovhcloud.com/sso/saml/sp/metadata.xml"
}
]

projects = [
for project in local.projects_data : merge(local.project_defaults, project)
]

oidc_projects = [
for project in local.projects : project if project.protocol == "oidc"
]

saml_projects = [
for project in local.projects : project if project.protocol == "saml"
]
}

resource "zitadel_project" "projects" {
Expand All @@ -57,7 +78,7 @@ resource "zitadel_project" "projects" {
}

resource "zitadel_application_oidc" "applications" {
for_each = { for project in local.projects : project.name => project }
for_each = { for project in local.oidc_projects : project.name => project }
name = upper(replace(each.value.name, "/[^a-zA-Z0-9]/", "_"))
org_id = zitadel_org.immich.id
project_id = zitadel_project.projects[each.key].id
Expand Down Expand Up @@ -91,3 +112,16 @@ resource "onepassword_item" "application_client_secret" {

password = each.value.client_secret
}

data "http" "saml_sp_metadata" {
for_each = { for project in local.saml_projects : project.name => project }
url = each.value.metadataUrl
}

resource "zitadel_application_saml" "applications" {
for_each = { for project in local.saml_projects : project.name => project }
name = upper(replace(each.value.name, "/[^a-zA-Z0-9]/", "_"))
org_id = zitadel_org.immich.id
project_id = zitadel_project.projects[each.key].id
metadata_xml = data.http.saml_sp_metadata[each.key].response_body
}
2 changes: 1 addition & 1 deletion tf/deployment/modules/shared/zitadel/users.tf
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@ resource "zitadel_user_metadata" "role" {
org_id = zitadel_org.immich.id
user_id = zitadel_human_user.users[each.key].id
key = "role"
value = each.value.role
value = jsonencode(each.value.roles)
}
Loading