Compare commits
	
		
			1 commit
		
	
	
		
			
				4467c2172d
			
			...
			
				5fee15c3dc
			
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5fee15c3dc | 
					 10 changed files with 38 additions and 124 deletions
				
			
		|  | @ -77,4 +77,3 @@ nix build .#proxmox-chaosknoten-nixos-template | ||||||
| ## License | ## License | ||||||
| 
 | 
 | ||||||
| This CCCHH nix-infra repository is licensed under the [MIT License](./LICENSE). | This CCCHH nix-infra repository is licensed under the [MIT License](./LICENSE). | ||||||
| [`0001_oidc_group_and_role_mapping_custom_pipeline.patch`](patches/0001_oidc_group_and_role_mapping_custom_pipeline.patch) is licensed under the Creative Commons: CC BY-SA 4.0 license. |  | ||||||
|  |  | ||||||
|  | @ -5,13 +5,13 @@ | ||||||
|     interfaces.net0 = { |     interfaces.net0 = { | ||||||
|       ipv4.addresses = [ |       ipv4.addresses = [ | ||||||
|         { |         { | ||||||
|           address = "172.31.200.14"; |           address = "10.31.210.10"; | ||||||
|           prefixLength = 23; |           prefixLength = 23; | ||||||
|         } |         } | ||||||
|       ]; |       ]; | ||||||
|     }; |     }; | ||||||
|     defaultGateway = "172.31.200.1"; |     defaultGateway = "10.31.210.1"; | ||||||
|     nameservers = [ "172.31.200.1" ]; |     nameservers = [ "10.31.210.1" ]; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   systemd.network.links."10-net0" = { |   systemd.network.links."10-net0" = { | ||||||
|  |  | ||||||
|  | @ -5,13 +5,13 @@ | ||||||
|     interfaces.net0 = { |     interfaces.net0 = { | ||||||
|       ipv4.addresses = [ |       ipv4.addresses = [ | ||||||
|         { |         { | ||||||
|           address = "172.31.200.15"; |           address = "10.31.210.13"; | ||||||
|           prefixLength = 23; |           prefixLength = 23; | ||||||
|         } |         } | ||||||
|       ]; |       ]; | ||||||
|     }; |     }; | ||||||
|     defaultGateway = "172.31.200.1"; |     defaultGateway = "10.31.210.1"; | ||||||
|     nameservers = [ "172.31.200.1" ]; |     nameservers = [ "10.31.210.1" ]; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   systemd.network.links."10-net0" = { |   systemd.network.links."10-net0" = { | ||||||
|  |  | ||||||
|  | @ -7,19 +7,13 @@ | ||||||
| # - https://codeberg.org/forgejo/forgejo/src/branch/forgejo/docs/content/administration/reverse-proxies.en-us.md | # - https://codeberg.org/forgejo/forgejo/src/branch/forgejo/docs/content/administration/reverse-proxies.en-us.md | ||||||
| # - https://forgejo.org/docs/latest/admin/email-setup/ | # - https://forgejo.org/docs/latest/admin/email-setup/ | ||||||
| 
 | 
 | ||||||
| { pkgs, ... }: | { pkgs-unstable, ... }: | ||||||
| 
 | 
 | ||||||
| { | { | ||||||
|   services.forgejo = { |   services.forgejo = { | ||||||
|     enable = true; |     enable = true; | ||||||
|     package = pkgs.forgejo; |  | ||||||
|     database.type = "postgres"; |     database.type = "postgres"; | ||||||
| 
 |     mailerPasswordFile = "/run/secrets/forgejo_git_smtp_password"; | ||||||
|     secrets = { |  | ||||||
|       mailer = { |  | ||||||
|         PASSWD = "/run/secrets/forgejo_git_smtp_password"; |  | ||||||
|       }; |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     settings = { |     settings = { | ||||||
|       DEFAULT = { |       DEFAULT = { | ||||||
|  |  | ||||||
|  | @ -9,8 +9,7 @@ | ||||||
| { | { | ||||||
|   services.netbox = { |   services.netbox = { | ||||||
|     enable = true; |     enable = true; | ||||||
|     # Explicitly use the patched NetBox package. |     package = pkgs.netbox; | ||||||
|     package = pkgs.netbox_4_1; |  | ||||||
|     secretKeyFile = "/run/secrets/netbox_secret_key"; |     secretKeyFile = "/run/secrets/netbox_secret_key"; | ||||||
|     keycloakClientSecret = "/run/secrets/netbox_keycloak_secret"; |     keycloakClientSecret = "/run/secrets/netbox_keycloak_secret"; | ||||||
|     settings = { |     settings = { | ||||||
|  | @ -25,24 +24,6 @@ | ||||||
|       SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi/Shi+b2OyYNGVFPsa6qf9SesEpRl5U5rpwgmt8H7NawMvwpPUYVW9o46QW0ulYcDmysT3BzpP3tagO/SFNoOjZdYe0D9nJ7vEp8KHbzR09KCfkyQIi0wLssKnDotVHL5JeUY+iKk+gjiwF9FSFSHPBqsST7hXVAut9LkOvs2aDod9AzbTH/uYbt4wfUm5l/1Ii8D+K7YcsFGUIqxv4XS/ylKqObqN4M2dac69iIwapoh6reaBQEm66vrOzJ+3yi4DZuPrkShJqi2hddtoyZihyCkF+eJJKEI5LrBf1KZB3Ec2YUrqk93ZGUGs/XY6R87QSfR3hJ82B1wnF+c2pw+QIDAQAB"; |       SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi/Shi+b2OyYNGVFPsa6qf9SesEpRl5U5rpwgmt8H7NawMvwpPUYVW9o46QW0ulYcDmysT3BzpP3tagO/SFNoOjZdYe0D9nJ7vEp8KHbzR09KCfkyQIi0wLssKnDotVHL5JeUY+iKk+gjiwF9FSFSHPBqsST7hXVAut9LkOvs2aDod9AzbTH/uYbt4wfUm5l/1Ii8D+K7YcsFGUIqxv4XS/ylKqObqN4M2dac69iIwapoh6reaBQEm66vrOzJ+3yi4DZuPrkShJqi2hddtoyZihyCkF+eJJKEI5LrBf1KZB3Ec2YUrqk93ZGUGs/XY6R87QSfR3hJ82B1wnF+c2pw+QIDAQAB"; | ||||||
|       SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = "https://id.hamburg.ccc.de/realms/ccchh/protocol/openid-connect/auth"; |       SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = "https://id.hamburg.ccc.de/realms/ccchh/protocol/openid-connect/auth"; | ||||||
|       SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = "https://id.hamburg.ccc.de/realms/ccchh/protocol/openid-connect/token"; |       SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = "https://id.hamburg.ccc.de/realms/ccchh/protocol/openid-connect/token"; | ||||||
|       SOCIAL_AUTH_PIPELINE = [ |  | ||||||
|         # The default pipeline as can be found in: |  | ||||||
|         # /nix/store/q2jsn56bgkj0nkz0j4w48x3klyn2x4gp-netbox-4.1.7/opt/netbox/netbox/netbox/settings.py |  | ||||||
|         "social_core.pipeline.social_auth.social_details" |  | ||||||
|         "social_core.pipeline.social_auth.social_uid" |  | ||||||
|         "social_core.pipeline.social_auth.social_user" |  | ||||||
|         "social_core.pipeline.user.get_username" |  | ||||||
|         "social_core.pipeline.user.create_user" |  | ||||||
|         "social_core.pipeline.social_auth.associate_user" |  | ||||||
|         "netbox.authentication.user_default_groups_handler" |  | ||||||
|         "social_core.pipeline.social_auth.load_extra_data" |  | ||||||
|         "social_core.pipeline.user.user_details" |  | ||||||
|         # Use custom pipeline functions patched in via netbox41OIDCMappingOverlay. |  | ||||||
|         # See: https://docs.goauthentik.io/integrations/services/netbox/ |  | ||||||
|         "netbox.custom_pipeline.add_groups" |  | ||||||
|         "netbox.custom_pipeline.remove_groups" |  | ||||||
|         "netbox.custom_pipeline.set_roles" |  | ||||||
|       ]; |  | ||||||
|     }; |     }; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,7 +22,6 @@ | ||||||
|       WOODPECKER_DATABASE_DATASOURCE = "postgresql://woodpecker-server@/woodpecker-server?host=/run/postgresql"; |       WOODPECKER_DATABASE_DATASOURCE = "postgresql://woodpecker-server@/woodpecker-server?host=/run/postgresql"; | ||||||
|       WOODPECKER_FORGEJO = "true"; |       WOODPECKER_FORGEJO = "true"; | ||||||
|       WOODPECKER_FORGEJO_URL = "https://git.hamburg.ccc.de"; |       WOODPECKER_FORGEJO_URL = "https://git.hamburg.ccc.de"; | ||||||
|       WOODPECKER_LIMIT_MEM = "6442450944"; # 6GB |  | ||||||
|       # Set via enviornmentFile: |       # Set via enviornmentFile: | ||||||
|       # WOODPECKER_FORGEJO_CLIENT |       # WOODPECKER_FORGEJO_CLIENT | ||||||
|       # WOODPECKER_FORGEJO_SECRET |       # WOODPECKER_FORGEJO_SECRET | ||||||
|  |  | ||||||
							
								
								
									
										32
									
								
								flake.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								flake.lock
									
										
									
										generated
									
									
									
								
							|  | @ -16,11 +16,11 @@ | ||||||
|     }, |     }, | ||||||
|     "nixlib": { |     "nixlib": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1734829460, |         "lastModified": 1731200463, | ||||||
|         "narHash": "sha256-dPhc+f2wkmhMqMIfq+hColJdysgVxKP9ilZ5bR0NRZI=", |         "narHash": "sha256-qDaAweJjdFbVExqs8aG27urUgcgKufkIngHW3Rzustg=", | ||||||
|         "owner": "nix-community", |         "owner": "nix-community", | ||||||
|         "repo": "nixpkgs.lib", |         "repo": "nixpkgs.lib", | ||||||
|         "rev": "0a31e8d833173ae63e43fd9dbff1ccf09c4f778c", |         "rev": "e04234d263750db01c78a412690363dc2226e68a", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|  | @ -35,11 +35,11 @@ | ||||||
|         "nixpkgs": "nixpkgs" |         "nixpkgs": "nixpkgs" | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1734915500, |         "lastModified": 1731546190, | ||||||
|         "narHash": "sha256-A7CTIQ8SW0hfbhKlwK+vSsu4pD+Oaelw3v6goX6go+U=", |         "narHash": "sha256-kJix8nLyFIJ3EC7VtoXK/85C4ZN2dC5oWoS8+ErehqI=", | ||||||
|         "owner": "nix-community", |         "owner": "nix-community", | ||||||
|         "repo": "nixos-generators", |         "repo": "nixos-generators", | ||||||
|         "rev": "051d1b2dda3b2e81b38d82e2b691e5c2f4d335f4", |         "rev": "06ffce1a8d95e95c06a4bcfa117dd960b14a7101", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|  | @ -50,11 +50,11 @@ | ||||||
|     }, |     }, | ||||||
|     "nixpkgs": { |     "nixpkgs": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1734435836, |         "lastModified": 1731245184, | ||||||
|         "narHash": "sha256-kMBQ5PRiFLagltK0sH+08aiNt3zGERC2297iB6vrvlU=", |         "narHash": "sha256-vmLS8+x+gHRv1yzj3n+GTAEObwmhxmkkukB2DwtJRdU=", | ||||||
|         "owner": "NixOS", |         "owner": "NixOS", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "4989a246d7a390a859852baddb1013f825435cee", |         "rev": "aebe249544837ce42588aa4b2e7972222ba12e8f", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|  | @ -66,16 +66,16 @@ | ||||||
|     }, |     }, | ||||||
|     "nixpkgs_2": { |     "nixpkgs_2": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1736408508, |         "lastModified": 1731842749, | ||||||
|         "narHash": "sha256-WIGZ3DPw5H+SPszUXVacK+KTh3sJZShP1vGtDwhquNM=", |         "narHash": "sha256-aNc8irVBH7sM5cGDvqdOueg8S+fGakf0rEMRGfGwWZw=", | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "530de2c83360057c1650fb8a37ef48cb9ad8f6a6", |         "rev": "bf6132dc791dbdff8b6894c3a85eb27ad8255682", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "ref": "nixos-24.11-small", |         "ref": "nixos-24.05-small", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       } |       } | ||||||
|  | @ -95,11 +95,11 @@ | ||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1736203741, |         "lastModified": 1731862312, | ||||||
|         "narHash": "sha256-eSjkBwBdQk+TZWFlLbclF2rAh4JxbGg8az4w/Lfe7f4=", |         "narHash": "sha256-NVUTFxKrJp/hjehlF1IvkPnlRYg/O9HFVutbxOM8zNM=", | ||||||
|         "owner": "Mic92", |         "owner": "Mic92", | ||||||
|         "repo": "sops-nix", |         "repo": "sops-nix", | ||||||
|         "rev": "c9c88f08e3ee495e888b8d7c8624a0b2519cb773", |         "rev": "472741cf3fee089241ac9ea705bb2b9e0bfa2978", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								flake.nix
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								flake.nix
									
										
									
									
									
								
							|  | @ -5,7 +5,7 @@ | ||||||
|     # Use the NixOS small channels for nixpkgs. |     # Use the NixOS small channels for nixpkgs. | ||||||
|     # https://nixos.org/manual/nixos/stable/#sec-upgrading |     # https://nixos.org/manual/nixos/stable/#sec-upgrading | ||||||
|     # https://github.com/NixOS/nixpkgs |     # https://github.com/NixOS/nixpkgs | ||||||
|     nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11-small"; |     nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05-small"; | ||||||
| 
 | 
 | ||||||
|     # Add nixos-generators as an input. |     # Add nixos-generators as an input. | ||||||
|     # See here: https://github.com/nix-community/nixos-generators#using-in-a-flake |     # See here: https://github.com/nix-community/nixos-generators#using-in-a-flake | ||||||
|  | @ -35,24 +35,27 @@ | ||||||
|       system = "x86_64-linux"; |       system = "x86_64-linux"; | ||||||
|     in |     in | ||||||
|     { |     { | ||||||
|  |       overlays = { | ||||||
|  |         shairportSyncAirplay2 = final: prev: { | ||||||
|  |           shairport-sync = (prev.shairport-sync.override { enableMetadata = true; enableAirplay2 = true; }).overrideAttrs (finalAttr: previousAttr: { | ||||||
|  |             # See: https://github.com/mikebrady/shairport-sync/blob/e78a88b64adfe7b5f88fd6faedf55c57445bb240/CONFIGURATION%20FLAGS.md | ||||||
|  |             configureFlags = previousAttr.configureFlags ++ [ "--with-mqtt-client" ]; | ||||||
|  |             buildInputs = previousAttr.buildInputs ++ [ final.mosquitto ]; | ||||||
|  |           }); | ||||||
|  |         }; | ||||||
|  |       }; | ||||||
|       nixosModules = { |       nixosModules = { | ||||||
|         common = ./config/common; |         common = ./config/common; | ||||||
|         proxmox-vm = ./config/proxmox-vm; |         proxmox-vm = ./config/proxmox-vm; | ||||||
|         prometheus-exporter = ./config/extra/prometheus-exporter.nix; |         prometheus-exporter = ./config/extra/prometheus-exporter.nix; | ||||||
|       }; |       }; | ||||||
|       overlays = { |  | ||||||
|         netbox41OIDCMappingOverlay = final: prev: { |  | ||||||
|           netbox_4_1 = prev.netbox_4_1.overrideAttrs (finalAttr: previousAttr: { |  | ||||||
|             patches = previousAttr.patches ++ [ ./patches/0001_oidc_group_and_role_mapping_custom_pipeline.patch ]; |  | ||||||
|           }); |  | ||||||
|         }; |  | ||||||
|       }; |  | ||||||
|       nixosConfigurations = { |       nixosConfigurations = { | ||||||
|         audio-hauptraum-kueche = nixpkgs.lib.nixosSystem { |         audio-hauptraum-kueche = nixpkgs.lib.nixosSystem { | ||||||
|           inherit system specialArgs; |           inherit system specialArgs; | ||||||
|           modules = [ |           modules = [ | ||||||
|             self.nixosModules.common |             self.nixosModules.common | ||||||
|             self.nixosModules.proxmox-vm |             self.nixosModules.proxmox-vm | ||||||
|  |             { nixpkgs.overlays = [ self.overlays.shairportSyncAirplay2 ]; } | ||||||
|             ./config/hosts/audio-hauptraum-kueche |             ./config/hosts/audio-hauptraum-kueche | ||||||
|           ]; |           ]; | ||||||
|         }; |         }; | ||||||
|  | @ -62,6 +65,7 @@ | ||||||
|           modules = [ |           modules = [ | ||||||
|             self.nixosModules.common |             self.nixosModules.common | ||||||
|             self.nixosModules.proxmox-vm |             self.nixosModules.proxmox-vm | ||||||
|  |             { nixpkgs.overlays = [ self.overlays.shairportSyncAirplay2 ]; } | ||||||
|             ./config/hosts/audio-hauptraum-tafel |             ./config/hosts/audio-hauptraum-tafel | ||||||
|           ]; |           ]; | ||||||
|         }; |         }; | ||||||
|  | @ -92,7 +96,6 @@ | ||||||
|             sops-nix.nixosModules.sops |             sops-nix.nixosModules.sops | ||||||
|             self.nixosModules.prometheus-exporter |             self.nixosModules.prometheus-exporter | ||||||
|             ./config/hosts/netbox |             ./config/hosts/netbox | ||||||
|             { nixpkgs.overlays = [ self.overlays.netbox41OIDCMappingOverlay ]; } |  | ||||||
|           ]; |           ]; | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,7 +17,6 @@ in | ||||||
|   config = mkIf cfg.enable { |   config = mkIf cfg.enable { | ||||||
|     services.shairport-sync = { |     services.shairport-sync = { | ||||||
|       enable = true; |       enable = true; | ||||||
|       package = pkgs.shairport-sync-airplay2; |  | ||||||
|       arguments = "-o pw -v"; |       arguments = "-o pw -v"; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,61 +0,0 @@ | ||||||
| diff --git a/netbox/netbox/custom_pipeline.py b/netbox/netbox/custom_pipeline.py
 |  | ||||||
| new file mode 100644 |  | ||||||
| index 000000000..470f388dc
 |  | ||||||
| --- /dev/null
 |  | ||||||
| +++ b/netbox/netbox/custom_pipeline.py
 |  | ||||||
| @@ -0,0 +1,55 @@
 |  | ||||||
| +# Licensed under Creative Commons: CC BY-SA 4.0 license.
 |  | ||||||
| +# https://github.com/goauthentik/authentik/blob/main/LICENSE
 |  | ||||||
| +# https://github.com/goauthentik/authentik/blob/main/website/integrations/services/netbox/index.md
 |  | ||||||
| +# https://docs.goauthentik.io/integrations/services/netbox/
 |  | ||||||
| +from netbox.authentication import Group
 |  | ||||||
| +
 |  | ||||||
| +class AuthFailed(Exception):
 |  | ||||||
| +    pass
 |  | ||||||
| +
 |  | ||||||
| +def add_groups(response, user, backend, *args, **kwargs):
 |  | ||||||
| +    try:
 |  | ||||||
| +        groups = response['groups']
 |  | ||||||
| +    except KeyError:
 |  | ||||||
| +        pass
 |  | ||||||
| +
 |  | ||||||
| +    # Add all groups from oAuth token
 |  | ||||||
| +    for group in groups:
 |  | ||||||
| +        group, created = Group.objects.get_or_create(name=group)
 |  | ||||||
| +        user.groups.add(group)
 |  | ||||||
| +
 |  | ||||||
| +def remove_groups(response, user, backend, *args, **kwargs):
 |  | ||||||
| +    try:
 |  | ||||||
| +        groups = response['groups']
 |  | ||||||
| +    except KeyError:
 |  | ||||||
| +        # Remove all groups if no groups in oAuth token
 |  | ||||||
| +        user.groups.clear()
 |  | ||||||
| +        pass
 |  | ||||||
| +
 |  | ||||||
| +    # Get all groups of user
 |  | ||||||
| +    user_groups = [item.name for item in user.groups.all()]
 |  | ||||||
| +    # Get groups of user which are not part of oAuth token
 |  | ||||||
| +    delete_groups = list(set(user_groups) - set(groups))
 |  | ||||||
| +
 |  | ||||||
| +    # Delete non oAuth token groups
 |  | ||||||
| +    for delete_group in delete_groups:
 |  | ||||||
| +        group = Group.objects.get(name=delete_group)
 |  | ||||||
| +        user.groups.remove(group)
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +def set_roles(response, user, backend, *args, **kwargs):
 |  | ||||||
| +    # Remove Roles temporary
 |  | ||||||
| +    user.is_superuser = False
 |  | ||||||
| +    user.is_staff = False
 |  | ||||||
| +    try:
 |  | ||||||
| +        groups = response['groups']
 |  | ||||||
| +    except KeyError:
 |  | ||||||
| +        # When no groups are set
 |  | ||||||
| +        # save the user without Roles
 |  | ||||||
| +        user.save()
 |  | ||||||
| +        pass
 |  | ||||||
| +
 |  | ||||||
| +    # Set roles is role (superuser or staff) is in groups
 |  | ||||||
| +    user.is_superuser = True if 'superusers' in groups else False
 |  | ||||||
| +    user.is_staff = True if 'staff' in groups else False
 |  | ||||||
| +    user.save()
 |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue