mirror of https://github.com/h44z/wg-portal.git
				
				
				
			Improve admin privilege handling for OAuth. Update documentation.
This commit is contained in:
		
							parent
							
								
									6523a87dfb
								
							
						
					
					
						commit
						662e9c0549
					
				
							
								
								
									
										6
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										6
									
								
								Makefile
								
								
								
								
							| 
						 | 
					@ -133,3 +133,9 @@ build-docker:
 | 
				
			||||||
.PHONY: helm-docs
 | 
					.PHONY: helm-docs
 | 
				
			||||||
helm-docs:
 | 
					helm-docs:
 | 
				
			||||||
	docker run --rm --volume "${PWD}/deploy:/helm-docs" -u "$$(id -u)" jnorwood/helm-docs -s file
 | 
						docker run --rm --volume "${PWD}/deploy:/helm-docs" -u "$$(id -u)" jnorwood/helm-docs -s file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#< run-mkdocs: Run a local instance of MkDocs
 | 
				
			||||||
 | 
					.PHONY: run-mkdocs
 | 
				
			||||||
 | 
					run-mkdocs:
 | 
				
			||||||
 | 
						python -m venv venv; source venv/bin/activate; pip install mike cairosvg mkdocs-material mkdocs-minify-plugin mkdocs-swagger-ui-tag
 | 
				
			||||||
 | 
						venv/bin/mkdocs serve
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								README.md
								
								
								
								
							
							
						
						
									
										20
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -56,7 +56,7 @@ By default, WireGuard Portal uses a SQLite database. The database is stored in *
 | 
				
			||||||
The following configuration options are available:
 | 
					The following configuration options are available:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| configuration key                | parent key | default_value                              | description                                                                                                                                       |
 | 
					| configuration key                | parent key | default_value                              | description                                                                                                                                       |
 | 
				
			||||||
|----------------------------------|------------|--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
 | 
					|----------------------------------|------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------|
 | 
				
			||||||
| admin_user                       | core       | admin@wgportal.local                       | The administrator user. This user will be created as default admin if it does not yet exist.                                                      |
 | 
					| admin_user                       | core       | admin@wgportal.local                       | The administrator user. This user will be created as default admin if it does not yet exist.                                                      |
 | 
				
			||||||
| admin_password                   | core       | wgportal                                   | The administrator password. If unchanged, a random password will be set on first startup.                                                         |
 | 
					| admin_password                   | core       | wgportal                                   | The administrator password. If unchanged, a random password will be set on first startup.                                                         |
 | 
				
			||||||
| editable_keys                    | core       | true                                       | Allow to edit key-pairs in the UI.                                                                                                                |
 | 
					| editable_keys                    | core       | true                                       | Allow to edit key-pairs in the UI.                                                                                                                |
 | 
				
			||||||
| 
						 | 
					@ -106,8 +106,10 @@ The following configuration options are available:
 | 
				
			||||||
| client_id                        | auth/oidc  |                                            | The OAuth client id.                                                                                                                              |
 | 
					| client_id                        | auth/oidc  |                                            | The OAuth client id.                                                                                                                              |
 | 
				
			||||||
| client_secret                    | auth/oidc  |                                            | The OAuth client secret.                                                                                                                          |
 | 
					| client_secret                    | auth/oidc  |                                            | The OAuth client secret.                                                                                                                          |
 | 
				
			||||||
| extra_scopes                     | auth/oidc  |                                            | Extra scopes that should be used in the OpenID Connect authentication flow.                                                                       |
 | 
					| extra_scopes                     | auth/oidc  |                                            | Extra scopes that should be used in the OpenID Connect authentication flow.                                                                       |
 | 
				
			||||||
| field_map                        | auth/oidc  |                                            | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and is_admin.                              |
 | 
					| field_map                        | auth/oidc  |                                            | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department, is_admin and user_groups.                |
 | 
				
			||||||
 | 
					| admin_mapping                    | auth/oidc  |                                            | Contains regex values admin_value_regex and admin_group_regex to map the is_admin field and user_groups respectively.                             |
 | 
				
			||||||
| registration_enabled             | auth/oidc  |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                                   |
 | 
					| registration_enabled             | auth/oidc  |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                                   |
 | 
				
			||||||
 | 
					| log_user_info                    | auth/oidc  |                                            | If true, the user info retrieved from the OIDC provider will be logged in trace level.                                                            |
 | 
				
			||||||
| provider_name                    | auth/oauth |                                            | A unique provider name. This name must be unique throughout all authentication providers (even other types).                                      |
 | 
					| provider_name                    | auth/oauth |                                            | A unique provider name. This name must be unique throughout all authentication providers (even other types).                                      |
 | 
				
			||||||
| display_name                     | auth/oauth |                                            | The display name is shown at the login page (the login button).                                                                                   |
 | 
					| display_name                     | auth/oauth |                                            | The display name is shown at the login page (the login button).                                                                                   |
 | 
				
			||||||
| client_id                        | auth/oauth |                                            | The OAuth client id.                                                                                                                              |
 | 
					| client_id                        | auth/oauth |                                            | The OAuth client id.                                                                                                                              |
 | 
				
			||||||
| 
						 | 
					@ -116,8 +118,10 @@ The following configuration options are available:
 | 
				
			||||||
| token_url                        | auth/oauth |                                            | The URL for the token endpoint.                                                                                                                   |
 | 
					| token_url                        | auth/oauth |                                            | The URL for the token endpoint.                                                                                                                   |
 | 
				
			||||||
| user_info_url                    | auth/oauth |                                            | The URL for the user information endpoint.                                                                                                        |
 | 
					| user_info_url                    | auth/oauth |                                            | The URL for the user information endpoint.                                                                                                        |
 | 
				
			||||||
| scopes                           | auth/oauth |                                            | OAuth scopes.                                                                                                                                     |
 | 
					| scopes                           | auth/oauth |                                            | OAuth scopes.                                                                                                                                     |
 | 
				
			||||||
| field_map                        | auth/oauth |                                            | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and is_admin.                              |
 | 
					| field_map                        | auth/oauth |                                            | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and is_admin and user_groups.             |
 | 
				
			||||||
 | 
					| admin_mapping                    | auth/oauth |                                            | Contains regex values admin_value_regex and admin_group_regex to map the is_admin field and user_groups respectively.                             |
 | 
				
			||||||
| registration_enabled             | auth/oauth |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                                   |
 | 
					| registration_enabled             | auth/oauth |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                                   |
 | 
				
			||||||
 | 
					| log_user_info                    | auth/oauth |                                            | If true, the user info retrieved from the OAuth provider will be logged in trace level.                                                           |
 | 
				
			||||||
| url                              | auth/ldap  |                                            | The LDAP server url. For example: ldap://srv-ad01.company.local:389	                                                                              |
 | 
					| url                              | auth/ldap  |                                            | The LDAP server url. For example: ldap://srv-ad01.company.local:389	                                                                              |
 | 
				
			||||||
| start_tls                        | auth/ldap  |                                            | Use STARTTLS to encrypt LDAP requests.                                                                                                            |
 | 
					| start_tls                        | auth/ldap  |                                            | Use STARTTLS to encrypt LDAP requests.                                                                                                            |
 | 
				
			||||||
| cert_validation                  | auth/ldap  |                                            | Validate the LDAP server certificate.                                                                                                             |
 | 
					| cert_validation                  | auth/ldap  |                                            | Validate the LDAP server certificate.                                                                                                             |
 | 
				
			||||||
| 
						 | 
					@ -133,6 +137,7 @@ The following configuration options are available:
 | 
				
			||||||
| sync_filter                      | auth/ldap  |                                            | LDAP filters for users that should be synchronized to WireGuard Portal.                                                                           |
 | 
					| sync_filter                      | auth/ldap  |                                            | LDAP filters for users that should be synchronized to WireGuard Portal.                                                                           |
 | 
				
			||||||
| sync_interval                    | auth/ldap  |                                            | The time interval after which users will be synchronized from LDAP. Empty value or `0` disables synchronization.                                  |
 | 
					| sync_interval                    | auth/ldap  |                                            | The time interval after which users will be synchronized from LDAP. Empty value or `0` disables synchronization.                                  |
 | 
				
			||||||
| registration_enabled             | auth/ldap  |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                                   |
 | 
					| registration_enabled             | auth/ldap  |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                                   |
 | 
				
			||||||
 | 
					| log_user_info                    | auth/ldap  |                                            | If true, the user info retrieved from the LDAP provider will be logged in trace level.                                                            |
 | 
				
			||||||
| debug                            | database   | false                                      | Debug database statements (log each statement).                                                                                                   |
 | 
					| debug                            | database   | false                                      | Debug database statements (log each statement).                                                                                                   |
 | 
				
			||||||
| slow_query_threshold             | database   |                                            | A threshold for slow database queries. If the threshold is exceeded, a warning message will be logged.                                            |
 | 
					| slow_query_threshold             | database   |                                            | A threshold for slow database queries. If the threshold is exceeded, a warning message will be logged.                                            |
 | 
				
			||||||
| type                             | database   | sqlite                                     | The database type. Allowed values: sqlite, mssql, mysql or postgres.                                                                              |
 | 
					| type                             | database   | sqlite                                     | The database type. Allowed values: sqlite, mssql, mysql or postgres.                                                                              |
 | 
				
			||||||
| 
						 | 
					@ -148,6 +153,10 @@ The following configuration options are available:
 | 
				
			||||||
| cert_file                        | web        |                                            | (Optional) Path to the TLS certificate file                                                                                                       |
 | 
					| cert_file                        | web        |                                            | (Optional) Path to the TLS certificate file                                                                                                       |
 | 
				
			||||||
| key_file                         | web        |                                            | (Optional) Path to the TLS certificate key file                                                                                                   |
 | 
					| key_file                         | web        |                                            | (Optional) Path to the TLS certificate key file                                                                                                   |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A sample config file can be found in the repository: [config.yml.sample](config.yml.sample). 
 | 
				
			||||||
 | 
					More detailed information about the configuration can be found in the [documentation](https://wgportal.org/master/documentation/overview/) on [wgportal.org](https://wgportal.org/master/documentation/overview/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Upgrading from V1
 | 
					## Upgrading from V1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> :warning: Before upgrading from V1, make sure that you have a backup of your currently working configuration files and database!
 | 
					> :warning: Before upgrading from V1, make sure that you have a backup of your currently working configuration files and database!
 | 
				
			||||||
| 
						 | 
					@ -173,16 +182,13 @@ Ensure that the new database does not contain any data!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## V2 TODOs
 | 
					## V2 TODOs
 | 
				
			||||||
 * Public REST API
 | 
					 | 
				
			||||||
 * Translations
 | 
					 | 
				
			||||||
 * Documentation
 | 
					 | 
				
			||||||
 * Audit UI
 | 
					 * Audit UI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Building
 | 
					## Building
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To build a standalone application, use the Makefile provided in the repository. 
 | 
					To build a standalone application, use the Makefile provided in the repository. 
 | 
				
			||||||
Go version 1.22 or higher has to be installed to build WireGuard Portal. 
 | 
					Go version 1.23 or higher has to be installed to build WireGuard Portal. 
 | 
				
			||||||
If you want to re-compile the frontend, NodeJS 18 and NPM >= 9 is required.
 | 
					If you want to re-compile the frontend, NodeJS 18 and NPM >= 9 is required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```shell
 | 
					```shell
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,5 @@
 | 
				
			||||||
 | 
					# More information about the configuration can be found in the documentation: https://wgportal.org/master/documentation/overview/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
advanced:
 | 
					advanced:
 | 
				
			||||||
  log_level: trace
 | 
					  log_level: trace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +24,7 @@ auth:
 | 
				
			||||||
      base_dn: DC=YOURCOMPANY,DC=LOCAL
 | 
					      base_dn: DC=YOURCOMPANY,DC=LOCAL
 | 
				
			||||||
      login_filter: (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2))
 | 
					      login_filter: (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2))
 | 
				
			||||||
      admin_group: CN=WireGuardAdmins,OU=it,DC=YOURCOMPANY,DC=LOCAL
 | 
					      admin_group: CN=WireGuardAdmins,OU=it,DC=YOURCOMPANY,DC=LOCAL
 | 
				
			||||||
      synchronize: false
 | 
					      sync_interval: 0  # sync disabled
 | 
				
			||||||
      sync_filter: (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))
 | 
					      sync_filter: (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))
 | 
				
			||||||
      registration_enabled: true
 | 
					      registration_enabled: true
 | 
				
			||||||
  oidc:
 | 
					  oidc:
 | 
				
			||||||
| 
						 | 
					@ -63,5 +65,28 @@ auth:
 | 
				
			||||||
        email: email
 | 
					        email: email
 | 
				
			||||||
        firstname: name
 | 
					        firstname: name
 | 
				
			||||||
        user_identifier: sub
 | 
					        user_identifier: sub
 | 
				
			||||||
        is_admin: roles
 | 
					        is_admin: this-attribute-must-be-true
 | 
				
			||||||
      registration_enabled: true
 | 
					      registration_enabled: true
 | 
				
			||||||
 | 
					    - id: google_plain_oauth_with_groups
 | 
				
			||||||
 | 
					      provider_name: google4
 | 
				
			||||||
 | 
					      display_name: Login with</br>Google4
 | 
				
			||||||
 | 
					      client_id: another-client-id-1234.apps.googleusercontent.com
 | 
				
			||||||
 | 
					      client_secret: A_CLIENT_SECRET
 | 
				
			||||||
 | 
					      auth_url: https://accounts.google.com/o/oauth2/v2/auth
 | 
				
			||||||
 | 
					      token_url: https://oauth2.googleapis.com/token
 | 
				
			||||||
 | 
					      user_info_url: https://openidconnect.googleapis.com/v1/userinfo
 | 
				
			||||||
 | 
					      scopes:
 | 
				
			||||||
 | 
					        - openid
 | 
				
			||||||
 | 
					        - email
 | 
				
			||||||
 | 
					        - profile
 | 
				
			||||||
 | 
					        - i-want-some-groups
 | 
				
			||||||
 | 
					      field_map:
 | 
				
			||||||
 | 
					        email: email
 | 
				
			||||||
 | 
					        firstname: name
 | 
				
			||||||
 | 
					        user_identifier: sub
 | 
				
			||||||
 | 
					        user_groups: groups
 | 
				
			||||||
 | 
					      admin_mapping:
 | 
				
			||||||
 | 
					        admin_value_regex: ^true$
 | 
				
			||||||
 | 
					        admin_group_regex: ^admin-group-name$
 | 
				
			||||||
 | 
					      registration_enabled: true
 | 
				
			||||||
 | 
					      log_user_info: true
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,176 @@
 | 
				
			||||||
 | 
					Below are some sample YAML configurations demonstrating how to override some default values. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Basic Configuration
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					core:
 | 
				
			||||||
 | 
					  admin_user: test@example.com
 | 
				
			||||||
 | 
					  admin_password: password
 | 
				
			||||||
 | 
					  import_existing: false
 | 
				
			||||||
 | 
					  create_default_peer: true
 | 
				
			||||||
 | 
					  self_provisioning_allowed: true
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					web:
 | 
				
			||||||
 | 
					  site_title: My WireGuard Server
 | 
				
			||||||
 | 
					  site_company_name: My Company
 | 
				
			||||||
 | 
					  listening_address: :8080
 | 
				
			||||||
 | 
					  external_url: https://my.externa-domain.com
 | 
				
			||||||
 | 
					  csrf_secret: super-s3cr3t-csrf
 | 
				
			||||||
 | 
					  session_secret: super-s3cr3t-session
 | 
				
			||||||
 | 
					  request_logging: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					advanced:
 | 
				
			||||||
 | 
					  log_level: trace
 | 
				
			||||||
 | 
					  log_pretty: true
 | 
				
			||||||
 | 
					  log_json: false
 | 
				
			||||||
 | 
					  config_storage_path: /etc/wireguard
 | 
				
			||||||
 | 
					  expiry_check_interval: 5m
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					database:
 | 
				
			||||||
 | 
					  debug: true
 | 
				
			||||||
 | 
					  type: sqlite
 | 
				
			||||||
 | 
					  dsn: data/sqlite.db
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## LDAP Authentication and Synchronization Configuration
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					# ... (basic configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auth:
 | 
				
			||||||
 | 
					  ldap:
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # a sample LDAP provider with user sync enabled
 | 
				
			||||||
 | 
					    - id: ldap
 | 
				
			||||||
 | 
					      provider_name: Active Directory
 | 
				
			||||||
 | 
					      display_name: Login with</br>AD
 | 
				
			||||||
 | 
					      url: ldap://srv-ad1.company.local:389
 | 
				
			||||||
 | 
					      bind_user: ldap_wireguard@company.local
 | 
				
			||||||
 | 
					      bind_pass: super-s3cr3t-ldap
 | 
				
			||||||
 | 
					      base_dn: DC=COMPANY,DC=LOCAL
 | 
				
			||||||
 | 
					      login_filter: (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2))
 | 
				
			||||||
 | 
					      sync_interval: 15m
 | 
				
			||||||
 | 
					      sync_filter: (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))
 | 
				
			||||||
 | 
					      disable_missing: true
 | 
				
			||||||
 | 
					      field_map:
 | 
				
			||||||
 | 
					        user_identifier: sAMAccountName
 | 
				
			||||||
 | 
					        email: mail
 | 
				
			||||||
 | 
					        firstname: givenName
 | 
				
			||||||
 | 
					        lastname: sn
 | 
				
			||||||
 | 
					        phone: telephoneNumber
 | 
				
			||||||
 | 
					        department: department
 | 
				
			||||||
 | 
					        memberof: memberOf
 | 
				
			||||||
 | 
					      admin_group: CN=WireGuardAdmins,OU=Some-OU,DC=COMPANY,DC=LOCAL
 | 
				
			||||||
 | 
					      registration_enabled: true
 | 
				
			||||||
 | 
					      log_user_info: true
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## OpenID Connect (OIDC) Authentication Configuration
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					# ... (basic configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auth:
 | 
				
			||||||
 | 
					  oidc:
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # a sample provider where users with the attribute `wg_admin` set to `true` are considered as admins   
 | 
				
			||||||
 | 
					    - id: oidc-with-admin-attribute
 | 
				
			||||||
 | 
					      provider_name: google
 | 
				
			||||||
 | 
					      display_name: Login with</br>Google
 | 
				
			||||||
 | 
					      base_url: https://accounts.google.com
 | 
				
			||||||
 | 
					      client_id: the-client-id-1234.apps.googleusercontent.com
 | 
				
			||||||
 | 
					      client_secret: A_CLIENT_SECRET
 | 
				
			||||||
 | 
					      extra_scopes:
 | 
				
			||||||
 | 
					        - https://www.googleapis.com/auth/userinfo.email
 | 
				
			||||||
 | 
					        - https://www.googleapis.com/auth/userinfo.profile
 | 
				
			||||||
 | 
					      field_map:
 | 
				
			||||||
 | 
					        user_identifier: sub
 | 
				
			||||||
 | 
					        email: email
 | 
				
			||||||
 | 
					        firstname: given_name
 | 
				
			||||||
 | 
					        lastname: family_name
 | 
				
			||||||
 | 
					        phone: phone_number
 | 
				
			||||||
 | 
					        department: department
 | 
				
			||||||
 | 
					        is_admin: wg_admin
 | 
				
			||||||
 | 
					      admin_mapping:
 | 
				
			||||||
 | 
					        - admin_value_regex: ^true$
 | 
				
			||||||
 | 
					      registration_enabled: true
 | 
				
			||||||
 | 
					      log_user_info: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # a sample provider where users in the group `the-admin-group` are considered as admins    
 | 
				
			||||||
 | 
					    - id: oidc-with-admin-group
 | 
				
			||||||
 | 
					      provider_name: google2
 | 
				
			||||||
 | 
					      display_name: Login with</br>Google2
 | 
				
			||||||
 | 
					      base_url: https://accounts.google.com
 | 
				
			||||||
 | 
					      client_id: another-client-id-1234.apps.googleusercontent.com
 | 
				
			||||||
 | 
					      client_secret: A_CLIENT_SECRET
 | 
				
			||||||
 | 
					      extra_scopes:
 | 
				
			||||||
 | 
					        - https://www.googleapis.com/auth/userinfo.email
 | 
				
			||||||
 | 
					        - https://www.googleapis.com/auth/userinfo.profile
 | 
				
			||||||
 | 
					      field_map:
 | 
				
			||||||
 | 
					        user_identifier: sub
 | 
				
			||||||
 | 
					        email: email
 | 
				
			||||||
 | 
					        firstname: given_name
 | 
				
			||||||
 | 
					        lastname: family_name
 | 
				
			||||||
 | 
					        phone: phone_number
 | 
				
			||||||
 | 
					        department: department
 | 
				
			||||||
 | 
					        user_groups: groups
 | 
				
			||||||
 | 
					      admin_mapping:
 | 
				
			||||||
 | 
					        - admin_group_regex: ^the-admin-group$
 | 
				
			||||||
 | 
					      registration_enabled: true
 | 
				
			||||||
 | 
					      log_user_info: true
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Plain OAuth2 Authentication Configuration
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					# ... (basic configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auth:
 | 
				
			||||||
 | 
					  oauth:
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # a sample provider where users with the attribute `this-attribute-must-be-true` set to `true` or `True`
 | 
				
			||||||
 | 
					    # are considered as admins    
 | 
				
			||||||
 | 
					    - id: google_plain_oauth-with-admin-attribute
 | 
				
			||||||
 | 
					      provider_name: google3
 | 
				
			||||||
 | 
					      display_name: Login with</br>Google3
 | 
				
			||||||
 | 
					      client_id: another-client-id-1234.apps.googleusercontent.com
 | 
				
			||||||
 | 
					      client_secret: A_CLIENT_SECRET
 | 
				
			||||||
 | 
					      auth_url: https://accounts.google.com/o/oauth2/v2/auth
 | 
				
			||||||
 | 
					      token_url: https://oauth2.googleapis.com/token
 | 
				
			||||||
 | 
					      user_info_url: https://openidconnect.googleapis.com/v1/userinfo
 | 
				
			||||||
 | 
					      scopes:
 | 
				
			||||||
 | 
					        - openid
 | 
				
			||||||
 | 
					        - email
 | 
				
			||||||
 | 
					        - profile
 | 
				
			||||||
 | 
					      field_map:
 | 
				
			||||||
 | 
					        user_identifier: sub
 | 
				
			||||||
 | 
					        email: email
 | 
				
			||||||
 | 
					        firstname: name
 | 
				
			||||||
 | 
					        is_admin: this-attribute-must-be-true
 | 
				
			||||||
 | 
					      admin_mapping:
 | 
				
			||||||
 | 
					        - admin_value_regex: ^(True|true)$
 | 
				
			||||||
 | 
					      registration_enabled: true
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # a sample provider where either users with the attribute `this-attribute-must-be-true` set to `true` or 
 | 
				
			||||||
 | 
					    # users in the group `admin-group-name` are considered as admins    
 | 
				
			||||||
 | 
					    - id: google_plain_oauth_with_groups
 | 
				
			||||||
 | 
					      provider_name: google4
 | 
				
			||||||
 | 
					      display_name: Login with</br>Google4
 | 
				
			||||||
 | 
					      client_id: another-client-id-1234.apps.googleusercontent.com
 | 
				
			||||||
 | 
					      client_secret: A_CLIENT_SECRET
 | 
				
			||||||
 | 
					      auth_url: https://accounts.google.com/o/oauth2/v2/auth
 | 
				
			||||||
 | 
					      token_url: https://oauth2.googleapis.com/token
 | 
				
			||||||
 | 
					      user_info_url: https://openidconnect.googleapis.com/v1/userinfo
 | 
				
			||||||
 | 
					      scopes:
 | 
				
			||||||
 | 
					        - openid
 | 
				
			||||||
 | 
					        - email
 | 
				
			||||||
 | 
					        - profile
 | 
				
			||||||
 | 
					        - i-want-some-groups
 | 
				
			||||||
 | 
					      field_map:
 | 
				
			||||||
 | 
					        email: email
 | 
				
			||||||
 | 
					        firstname: name
 | 
				
			||||||
 | 
					        user_identifier: sub
 | 
				
			||||||
 | 
					        is_admin: this-attribute-must-be-true
 | 
				
			||||||
 | 
					        user_groups: groups
 | 
				
			||||||
 | 
					      admin_mapping:
 | 
				
			||||||
 | 
					        admin_value_regex: ^true$
 | 
				
			||||||
 | 
					        admin_group_regex: ^admin-group-name$
 | 
				
			||||||
 | 
					      registration_enabled: true
 | 
				
			||||||
 | 
					      log_user_info: true
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,453 @@
 | 
				
			||||||
 | 
					# WireGuard Portal Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This page provides an overview of **all available configuration options** for WireGuard Portal. 
 | 
				
			||||||
 | 
					You can supply these configurations in a **YAML** file (e.g. `config.yaml`) when starting the Portal.
 | 
				
			||||||
 | 
					Complete configuration examples are available in the [Configuration Examples](./examples.md) page.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Below you will find sections like `core`, `advanced`, `statistics`, `mail`, `auth`, `database`, and `web`.  
 | 
				
			||||||
 | 
					Each section describes the individual configuration keys, their default values, and a brief explanation of their purpose.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These are the primary configuration options that control fundamental WireGuard Portal behavior. 
 | 
				
			||||||
 | 
					More advanced options are found in the subsequent `Advanced` section.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `admin_user`
 | 
				
			||||||
 | 
					- **Default:** `admin@wgportal.local`
 | 
				
			||||||
 | 
					- **Description:** The administrator user. This user will be created as a default admin if it does not yet exist.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `admin_password`
 | 
				
			||||||
 | 
					- **Default:** `wgportal`
 | 
				
			||||||
 | 
					- **Description:** The administrator password. The default password of `wgportal` should be changed immediately.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `editable_keys`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** Allow editing of WireGuard key-pairs directly in the UI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `create_default_peer`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If a user logs in for the first time with no existing peers, automatically create a new WireGuard peer for **all** server interfaces.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `create_default_peer_on_creation`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If an LDAP user is created (e.g., through LDAP sync) and has no peers, automatically create a new WireGuard peer for **all** server interfaces.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `re_enable_peer_after_user_enable`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** Re-enable all peers that were previously disabled if the associated user is re-enabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `delete_peer_after_user_deleted`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If a user is deleted, remove all linked peers. Otherwise, peers remain but are disabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `self_provisioning_allowed`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** Allow registered (non-admin) users to self-provision peers from their profile page.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `import_existing`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** On startup, import existing WireGuard interfaces and peers into WireGuard Portal.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `restore_state`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** Restore the WireGuard interface states (up/down) that existed before WireGuard Portal started.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Advanced
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Additional or more specialized configuration options for logging and interface creation details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `log_level`
 | 
				
			||||||
 | 
					- **Default:** `info`
 | 
				
			||||||
 | 
					- **Description:** The log level used by the application. Valid options are: `trace`, `debug`, `info`, `warn`, `error`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `log_pretty`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If `true`, log messages are colorized and formatted for readability (pretty-print).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `log_json`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If `true`, log messages are structured in JSON format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `start_listen_port`
 | 
				
			||||||
 | 
					- **Default:** `51820`
 | 
				
			||||||
 | 
					- **Description:** The first port to use when automatically creating new WireGuard interfaces.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `start_cidr_v4`
 | 
				
			||||||
 | 
					- **Default:** `10.11.12.0/24`
 | 
				
			||||||
 | 
					- **Description:** The initial IPv4 subnet to use when automatically creating new WireGuard interfaces.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `start_cidr_v6`
 | 
				
			||||||
 | 
					- **Default:** `fdfd:d3ad:c0de:1234::0/64`
 | 
				
			||||||
 | 
					- **Description:** The initial IPv6 subnet to use when automatically creating new WireGuard interfaces.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `use_ip_v6`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** Enable or disable IPv6 support.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `config_storage_path`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Path to a directory where `wg-quick` style configuration files will be stored (if you need local filesystem configs).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `expiry_check_interval`
 | 
				
			||||||
 | 
					- **Default:** `15m`
 | 
				
			||||||
 | 
					- **Description:** Interval after which existing peers are checked if they are expired. Format uses `s`, `m`, `h`, `d` for seconds, minutes, hours, days, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `rule_prio_offset`
 | 
				
			||||||
 | 
					- **Default:** `20000`
 | 
				
			||||||
 | 
					- **Description:** Offset for IP route rule priorities when configuring routing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `route_table_offset`
 | 
				
			||||||
 | 
					- **Default:** `20000`
 | 
				
			||||||
 | 
					- **Description:** Offset for IP route table IDs when configuring routing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `api_admin_only`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** If `true`, the public REST API is accessible only to admin users. The API docs live at [`/api/v1/doc.html`](../rest-api/api-doc.md).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Configuration for the underlying database used by WireGuard Portal. 
 | 
				
			||||||
 | 
					Supported databases include SQLite, MySQL, Microsoft SQL Server, and Postgres.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `debug`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If `true`, logs all database statements (verbose).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `slow_query_threshold`
 | 
				
			||||||
 | 
					- **Default:** 0
 | 
				
			||||||
 | 
					- **Description:** A time threshold (e.g., `100ms`) above which queries are considered slow and logged as warnings. If empty or zero, slow query logging is disabled. Format uses `s`, `ms` for seconds, milliseconds, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `type`
 | 
				
			||||||
 | 
					- **Default:** `sqlite`
 | 
				
			||||||
 | 
					- **Description:** The database type. Valid options: `sqlite`, `mssql`, `mysql`, `postgres`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `dsn`
 | 
				
			||||||
 | 
					- **Default:** `data/sqlite.db`
 | 
				
			||||||
 | 
					- **Description:** The Data Source Name (DSN) for connecting to the database.  
 | 
				
			||||||
 | 
					  For example:
 | 
				
			||||||
 | 
					  ```text
 | 
				
			||||||
 | 
					  user:pass@tcp(1.2.3.4:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Statistics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Controls how WireGuard Portal collects and reports usage statistics, including ping checks and Prometheus metrics.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `use_ping_checks`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** Enable periodic ping checks to verify that peers remain responsive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `ping_check_workers`
 | 
				
			||||||
 | 
					- **Default:** `10`
 | 
				
			||||||
 | 
					- **Description:** Number of parallel worker processes for ping checks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `ping_unprivileged`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If `false`, ping checks run without root privileges. This is currently considered BETA.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `ping_check_interval`
 | 
				
			||||||
 | 
					- **Default:** `1m`
 | 
				
			||||||
 | 
					- **Description:** Interval between consecutive ping checks for all peers. Format uses `s`, `m`, `h`, `d` for seconds, minutes, hours, days, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `data_collection_interval`
 | 
				
			||||||
 | 
					- **Default:** `1m`
 | 
				
			||||||
 | 
					- **Description:** Interval between data collection cycles (bytes sent/received, handshake times, etc.). Format uses `s`, `m`, `h`, `d` for seconds, minutes, hours, days, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `collect_interface_data`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** If `true`, collects interface-level data (bytes in/out) for monitoring and statistics.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `collect_peer_data`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** If `true`, collects peer-level data (bytes, last handshake, endpoint, etc.).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `collect_audit_data`
 | 
				
			||||||
 | 
					- **Default:** `true`
 | 
				
			||||||
 | 
					- **Description:** If `true`, logs certain portal events (such as user logins) to the database.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `listening_address`
 | 
				
			||||||
 | 
					- **Default:** `:8787`
 | 
				
			||||||
 | 
					- **Description:** Address and port for the integrated Prometheus metric server (e.g., `:8787`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Mail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Options for configuring email notifications or sending peer configurations via email.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `host`
 | 
				
			||||||
 | 
					- **Default:** `127.0.0.1`
 | 
				
			||||||
 | 
					- **Description:** Hostname or IP of the SMTP server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `port`
 | 
				
			||||||
 | 
					- **Default:** `25`
 | 
				
			||||||
 | 
					- **Description:** Port number for the SMTP server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `encryption`
 | 
				
			||||||
 | 
					- **Default:** `none`
 | 
				
			||||||
 | 
					- **Description:** SMTP encryption type. Valid values: `none`, `tls`, `starttls`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `cert_validation`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If `true`, validate the SMTP server certificate (relevant if `encryption` = `tls`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `username`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Optional SMTP username for authentication.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `password`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Optional SMTP password for authentication.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `auth_type`
 | 
				
			||||||
 | 
					- **Default:** `plain`
 | 
				
			||||||
 | 
					- **Description:** SMTP authentication type. Valid values: `plain`, `login`, `crammd5`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `from`
 | 
				
			||||||
 | 
					- **Default:** `Wireguard Portal <noreply@wireguard.local>`
 | 
				
			||||||
 | 
					- **Description:** The default "From" address when sending emails.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `link_only`
 | 
				
			||||||
 | 
					- **Default:** `false`
 | 
				
			||||||
 | 
					- **Description:** If `true`, emails only contain a link to WireGuard Portal, rather than attaching the full configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WireGuard Portal supports multiple authentication strategies, including **OpenID Connect** (`oidc`), **OAuth** (`oauth`), and **LDAP** (`ldap`). 
 | 
				
			||||||
 | 
					Each can have multiple providers configured. Below are the relevant keys.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### OIDC Provider Properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `oidc` array contains a list of OpenID Connect providers. 
 | 
				
			||||||
 | 
					Below are the properties for each OIDC provider entry inside `auth.oidc`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `provider_name`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** A **unique** name for this provider. Must not conflict with other providers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `display_name`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** A user-friendly name shown on the login page (e.g., "Login with Google").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `base_url`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The OIDC provider’s base URL (e.g., `https://accounts.google.com`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `client_id`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The OAuth client ID from the OIDC provider.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `client_secret`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The OAuth client secret from the OIDC provider.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `extra_scopes`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** A list of additional OIDC scopes (e.g., `profile`, `email`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `field_map`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Maps OIDC claims to WireGuard Portal user fields. 
 | 
				
			||||||
 | 
					  - Available fields: `user_identifier`, `email`, `firstname`, `lastname`, `phone`, `department`, `is_admin`, `user_groups`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    | **Field**         | **Typical OIDC Claim**            | **Explanation**                                                                                                                                                                                         |
 | 
				
			||||||
 | 
					    |-------------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | 
				
			||||||
 | 
					    | `user_identifier` | `sub` or `preferred_username`     | A unique identifier for the user. Often the OIDC `sub` claim is used because it’s guaranteed to be unique for the user within the IdP. Some providers also support `preferred_username` if it’s unique. |
 | 
				
			||||||
 | 
					    | `email`           | `email`                           | The user’s email address as provided by the IdP. Not always verified, depending on IdP settings.                                                                                                        |
 | 
				
			||||||
 | 
					    | `firstname`       | `given_name`                      | The user’s first name, typically provided by the IdP in the `given_name` claim.                                                                                                                         |
 | 
				
			||||||
 | 
					    | `lastname`        | `family_name`                     | The user’s last (family) name, typically provided by the IdP in the `family_name` claim.                                                                                                                |
 | 
				
			||||||
 | 
					    | `phone`           | `phone_number`                    | The user’s phone number. This may require additional scopes/permissions from the IdP to access.                                                                                                         |
 | 
				
			||||||
 | 
					    | `department`      | Custom claim (e.g., `department`) | If the IdP can provide organizational data, it may store it in a custom claim. Adjust accordingly (e.g., `department`, `org`, or another attribute).                                                    |
 | 
				
			||||||
 | 
					    | `is_admin`        | Custom claim or derived role      | If the IdP returns a role or admin flag, you can map that to `is_admin`. Often this is managed through custom claims or group membership.                                                               |
 | 
				
			||||||
 | 
					    | `user_groups`     | `groups` or another custom claim  | A list of group memberships for the user. Some IdPs provide `groups` out of the box; others require custom claims or directory lookups.                                                                 |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `admin_mapping`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** WgPortal can grant a user admin rights by matching the value of the `is_admin` claim against a regular expression. Alternatively, a regular expression can be used to check if a user is member of a specific group listed in the `user_group` claim. The regular expressions are defined in `admin_value_regex` and `admin_group_regex`.
 | 
				
			||||||
 | 
					    - `admin_value_regex`: A regular expression to match the `is_admin` claim. By default, this expression matches the string "true" (`^true$`).
 | 
				
			||||||
 | 
					    - `admin_group_regex`: A regular expression to match the `user_groups` claim. Each entry in the `user_groups` claim is checked against this regex.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `registration_enabled`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, a new user will be created in WireGuard Portal if not already present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `log_user_info`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, OIDC user data is logged at the trace level upon login (for debugging).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### OAuth Provider Properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `oauth` array contains a list of plain OAuth2 providers.
 | 
				
			||||||
 | 
					Below are the properties for each OAuth provider entry inside `auth.oauth`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `provider_name`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** A **unique** name for this provider. Must not conflict with other providers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `display_name`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** A user-friendly name shown on the login page.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `client_id`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The OAuth client ID for the provider.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `client_secret`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The OAuth client secret for the provider.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `auth_url`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** URL of the authentication endpoint.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `token_url`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** URL of the token endpoint.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `user_info_url`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** URL of the user information endpoint.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `scopes`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** A list of OAuth scopes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `field_map`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Maps OAuth attributes to WireGuard Portal fields.
 | 
				
			||||||
 | 
					  - Available fields: `user_identifier`, `email`, `firstname`, `lastname`, `phone`, `department`, `is_admin`, `user_groups`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    | **Field**         | **Typical Claim**                 | **Explanation**                                                                                                                                                                                         |
 | 
				
			||||||
 | 
					    |-------------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | 
				
			||||||
 | 
					    | `user_identifier` | `sub` or `preferred_username`     | A unique identifier for the user. Often the OIDC `sub` claim is used because it’s guaranteed to be unique for the user within the IdP. Some providers also support `preferred_username` if it’s unique. |
 | 
				
			||||||
 | 
					    | `email`           | `email`                           | The user’s email address as provided by the IdP. Not always verified, depending on IdP settings.                                                                                                        |
 | 
				
			||||||
 | 
					    | `firstname`       | `given_name`                      | The user’s first name, typically provided by the IdP in the `given_name` claim.                                                                                                                         |
 | 
				
			||||||
 | 
					    | `lastname`        | `family_name`                     | The user’s last (family) name, typically provided by the IdP in the `family_name` claim.                                                                                                                |
 | 
				
			||||||
 | 
					    | `phone`           | `phone_number`                    | The user’s phone number. This may require additional scopes/permissions from the IdP to access.                                                                                                         |
 | 
				
			||||||
 | 
					    | `department`      | Custom claim (e.g., `department`) | If the IdP can provide organizational data, it may store it in a custom claim. Adjust accordingly (e.g., `department`, `org`, or another attribute).                                                    |
 | 
				
			||||||
 | 
					    | `is_admin`        | Custom claim or derived role      | If the IdP returns a role or admin flag, you can map that to `is_admin`. Often this is managed through custom claims or group membership.                                                               |
 | 
				
			||||||
 | 
					    | `user_groups`     | `groups` or another custom claim  | A list of group memberships for the user. Some IdPs provide `groups` out of the box; others require custom claims or directory lookups.                                                                 |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `admin_mapping`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** WgPortal can grant a user admin rights by matching the value of the `is_admin` claim against a regular expression. Alternatively, a regular expression can be used to check if a user is member of a specific group listed in the `user_group` claim. The regular expressions are defined in `admin_value_regex` and `admin_group_regex`.
 | 
				
			||||||
 | 
					  - `admin_value_regex`: A regular expression to match the `is_admin` claim. By default, this expression matches the string "true" (`^true$`).
 | 
				
			||||||
 | 
					  - `admin_group_regex`: A regular expression to match the `user_groups` claim. Each entry in the `user_groups` claim is checked against this regex.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `registration_enabled`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, new users are created automatically on successful login.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `log_user_info`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, logs user info at the trace level upon login.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### LDAP Provider Properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `ldap` array contains a list of LDAP authentication providers.
 | 
				
			||||||
 | 
					Below are the properties for each LDAP provider entry inside `auth.ldap`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `url`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The LDAP server URL (e.g., `ldap://srv-ad01.company.local:389`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `start_tls`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, use STARTTLS to secure the LDAP connection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `cert_validation`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, validate the LDAP server’s TLS certificate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `tls_certificate_path`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Path to a TLS certificate if needed for LDAP connections.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `tls_key_path`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Path to the corresponding TLS certificate key.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `base_dn`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The base DN for user searches (e.g., `DC=COMPANY,DC=LOCAL`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `bind_user`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The bind user for LDAP (e.g., `company\\ldap_wireguard` or `ldap_wireguard@company.local`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `bind_pass`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** The bind password for LDAP authentication.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `field_map`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** Maps LDAP attributes to WireGuard Portal fields.
 | 
				
			||||||
 | 
					    - Available fields: `user_identifier`, `email`, `firstname`, `lastname`, `phone`, `department`, `memberof`.
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					      | **WireGuard Portal Field** | **Typical LDAP Attribute** | **Short Description**                                        |
 | 
				
			||||||
 | 
					      |----------------------------|----------------------------|--------------------------------------------------------------|
 | 
				
			||||||
 | 
					      | user_identifier            | sAMAccountName / uid       | Uniquely identifies the user within the LDAP directory.      |
 | 
				
			||||||
 | 
					      | email                      | mail / userPrincipalName   | Stores the user's primary email address.                     |
 | 
				
			||||||
 | 
					      | firstname                  | givenName                  | Contains the user's first (given) name.                      |
 | 
				
			||||||
 | 
					      | lastname                   | sn                         | Contains the user's last (surname) name.                     |
 | 
				
			||||||
 | 
					      | phone                      | telephoneNumber / mobile   | Holds the user's phone or mobile number.                     |
 | 
				
			||||||
 | 
					      | department                 | departmentNumber / ou      | Specifies the department or organizational unit of the user. |
 | 
				
			||||||
 | 
					      | memberof                   | memberOf                   | Lists the groups and roles to which the user belongs.        |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `login_filter`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** An LDAP filter to restrict which users can log in. Use `{{login_identifier}}` to insert the username.
 | 
				
			||||||
 | 
					  For example:
 | 
				
			||||||
 | 
					  ```text
 | 
				
			||||||
 | 
					  (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2))
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `admin_group`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** A specific LDAP group whose members are considered administrators in WireGuard Portal.
 | 
				
			||||||
 | 
					  For example:
 | 
				
			||||||
 | 
					  ```text
 | 
				
			||||||
 | 
					  CN=WireGuardAdmins,OU=Some-OU,DC=YOURDOMAIN,DC=LOCAL
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `sync_interval`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** How frequently (in duration, e.g. `30m`) to synchronize users from LDAP. Empty or `0` disables sync. Format uses `s`, `m`, `h`, `d` for seconds, minutes, hours, days, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration).
 | 
				
			||||||
 | 
					  Only users that match the `sync_filter` are synchronized, if `disable_missing` is `true`, users not found in LDAP are disabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `sync_filter`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** An LDAP filter to select which users get synchronized into WireGuard Portal.
 | 
				
			||||||
 | 
					  For example:
 | 
				
			||||||
 | 
					  ```text
 | 
				
			||||||
 | 
					  (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `disable_missing`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, any user **not** found in LDAP (during sync) is disabled in WireGuard Portal.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `registration_enabled`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, new user accounts are created in WireGuard Portal upon first login.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `log_user_info`
 | 
				
			||||||
 | 
					- **Default:** *(empty)*
 | 
				
			||||||
 | 
					- **Description:** If `true`, logs LDAP user data at the trace level upon login.
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
To build a standalone application, use the Makefile provided in the repository.
 | 
					To build a standalone application, use the Makefile provided in the repository.
 | 
				
			||||||
Go version **1.22** or higher has to be installed to build WireGuard Portal.
 | 
					Go version **1.23** or higher has to be installed to build WireGuard Portal.
 | 
				
			||||||
If you want to re-compile the frontend, NodeJS **18** and NPM >= **9** is required.
 | 
					If you want to re-compile the frontend, NodeJS **18** and NPM >= **9** is required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```shell
 | 
					```shell
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ A sample docker-compose.yml:
 | 
				
			||||||
version: '3.6'
 | 
					version: '3.6'
 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  wg-portal:
 | 
					  wg-portal:
 | 
				
			||||||
    image: wgportal/wg-portal:v2
 | 
					    image: wgportal/wg-portal:latest
 | 
				
			||||||
    restart: unless-stopped
 | 
					    restart: unless-stopped
 | 
				
			||||||
    cap_add:
 | 
					    cap_add:
 | 
				
			||||||
      - NET_ADMIN
 | 
					      - NET_ADMIN
 | 
				
			||||||
| 
						 | 
					@ -64,18 +64,4 @@ You should mount those directories as a volume:
 | 
				
			||||||
- /app/data
 | 
					- /app/data
 | 
				
			||||||
- /app/config
 | 
					- /app/config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Configuration Options
 | 
					A detailed description of the configuration options can be found [here](../configuration/overview.md).
 | 
				
			||||||
All available YAML configuration options are available [here](https://github.com/h44z/wg-portal#configuration).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A very basic example:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```yaml
 | 
					 | 
				
			||||||
core:
 | 
					 | 
				
			||||||
  admin_user: test@wg-portal.local
 | 
					 | 
				
			||||||
  admin_password: secret
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
web:
 | 
					 | 
				
			||||||
  external_url: http://localhost:8888
 | 
					 | 
				
			||||||
  request_logging: true
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,3 +23,14 @@ For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The upgrade will transform the old, existing database and store the values in the new database specified in the **config.yml** configuration file.
 | 
					The upgrade will transform the old, existing database and store the values in the new database specified in the **config.yml** configuration file.
 | 
				
			||||||
Ensure that the new database does not contain any data!
 | 
					Ensure that the new database does not contain any data!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you are using Docker, you can adapt the docker-compose.yml file to start the upgrade process:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  wg-portal:
 | 
				
			||||||
 | 
					    image: wgportal/wg-portal:latest
 | 
				
			||||||
 | 
					    # ... other settings
 | 
				
			||||||
 | 
					    restart: no
 | 
				
			||||||
 | 
					    command: ["-migrateFrom=/app/data/wg_portal.db"]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ package auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +10,7 @@ import (
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal"
 | 
						"github.com/h44z/wg-portal/internal"
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/config"
 | 
						"github.com/h44z/wg-portal/internal/config"
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/domain"
 | 
						"github.com/h44z/wg-portal/internal/domain"
 | 
				
			||||||
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type LdapAuthenticator struct {
 | 
					type LdapAuthenticator struct {
 | 
				
			||||||
| 
						 | 
					@ -78,7 +80,10 @@ func (l LdapAuthenticator) PlaintextAuthentication(userId domain.UserIdentifier,
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (l LdapAuthenticator) GetUserInfo(_ context.Context, userId domain.UserIdentifier) (map[string]interface{}, error) {
 | 
					func (l LdapAuthenticator) GetUserInfo(_ context.Context, userId domain.UserIdentifier) (
 | 
				
			||||||
 | 
						map[string]interface{},
 | 
				
			||||||
 | 
						error,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
	conn, err := internal.LdapConnect(l.cfg)
 | 
						conn, err := internal.LdapConnect(l.cfg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("failed to setup connection: %w", err)
 | 
							return nil, fmt.Errorf("failed to setup connection: %w", err)
 | 
				
			||||||
| 
						 | 
					@ -109,6 +114,11 @@ func (l LdapAuthenticator) GetUserInfo(_ context.Context, userId domain.UserIden
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	users := internal.LdapConvertEntries(sr, &l.cfg.FieldMap)
 | 
						users := internal.LdapConvertEntries(sr, &l.cfg.FieldMap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if l.cfg.LogUserInfo {
 | 
				
			||||||
 | 
							contents, _ := json.Marshal(users[0])
 | 
				
			||||||
 | 
							logrus.Tracef("User info from LDAP source %s for %s: %v", l.GetName(), userId, string(contents))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return users[0], nil
 | 
						return users[0], nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,12 +6,11 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal"
 | 
					 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/config"
 | 
						"github.com/h44z/wg-portal/internal/config"
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/domain"
 | 
						"github.com/h44z/wg-portal/internal/domain"
 | 
				
			||||||
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,10 +20,16 @@ type PlainOauthAuthenticator struct {
 | 
				
			||||||
	userInfoEndpoint    string
 | 
						userInfoEndpoint    string
 | 
				
			||||||
	client              *http.Client
 | 
						client              *http.Client
 | 
				
			||||||
	userInfoMapping     config.OauthFields
 | 
						userInfoMapping     config.OauthFields
 | 
				
			||||||
 | 
						userAdminMapping    *config.OauthAdminMapping
 | 
				
			||||||
	registrationEnabled bool
 | 
						registrationEnabled bool
 | 
				
			||||||
 | 
						userInfoLogging     bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newPlainOauthAuthenticator(_ context.Context, callbackUrl string, cfg *config.OAuthProvider) (*PlainOauthAuthenticator, error) {
 | 
					func newPlainOauthAuthenticator(
 | 
				
			||||||
 | 
						_ context.Context,
 | 
				
			||||||
 | 
						callbackUrl string,
 | 
				
			||||||
 | 
						cfg *config.OAuthProvider,
 | 
				
			||||||
 | 
					) (*PlainOauthAuthenticator, error) {
 | 
				
			||||||
	var provider = &PlainOauthAuthenticator{}
 | 
						var provider = &PlainOauthAuthenticator{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	provider.name = cfg.ProviderName
 | 
						provider.name = cfg.ProviderName
 | 
				
			||||||
| 
						 | 
					@ -44,7 +49,9 @@ func newPlainOauthAuthenticator(_ context.Context, callbackUrl string, cfg *conf
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	provider.userInfoEndpoint = cfg.UserInfoURL
 | 
						provider.userInfoEndpoint = cfg.UserInfoURL
 | 
				
			||||||
	provider.userInfoMapping = getOauthFieldMapping(cfg.FieldMap)
 | 
						provider.userInfoMapping = getOauthFieldMapping(cfg.FieldMap)
 | 
				
			||||||
 | 
						provider.userAdminMapping = &cfg.AdminMapping
 | 
				
			||||||
	provider.registrationEnabled = cfg.RegistrationEnabled
 | 
						provider.registrationEnabled = cfg.RegistrationEnabled
 | 
				
			||||||
 | 
						provider.userInfoLogging = cfg.LogUserInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return provider, nil
 | 
						return provider, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -65,11 +72,19 @@ func (p PlainOauthAuthenticator) AuthCodeURL(state string, opts ...oauth2.AuthCo
 | 
				
			||||||
	return p.cfg.AuthCodeURL(state, opts...)
 | 
						return p.cfg.AuthCodeURL(state, opts...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p PlainOauthAuthenticator) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
 | 
					func (p PlainOauthAuthenticator) Exchange(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						code string,
 | 
				
			||||||
 | 
						opts ...oauth2.AuthCodeOption,
 | 
				
			||||||
 | 
					) (*oauth2.Token, error) {
 | 
				
			||||||
	return p.cfg.Exchange(ctx, code, opts...)
 | 
						return p.cfg.Exchange(ctx, code, opts...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p PlainOauthAuthenticator) GetUserInfo(ctx context.Context, token *oauth2.Token, _ string) (map[string]interface{}, error) {
 | 
					func (p PlainOauthAuthenticator) GetUserInfo(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						token *oauth2.Token,
 | 
				
			||||||
 | 
						_ string,
 | 
				
			||||||
 | 
					) (map[string]interface{}, error) {
 | 
				
			||||||
	req, err := http.NewRequest("GET", p.userInfoEndpoint, nil)
 | 
						req, err := http.NewRequest("GET", p.userInfoEndpoint, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("failed to create user info get request: %w", err)
 | 
							return nil, fmt.Errorf("failed to create user info get request: %w", err)
 | 
				
			||||||
| 
						 | 
					@ -93,57 +108,13 @@ func (p PlainOauthAuthenticator) GetUserInfo(ctx context.Context, token *oauth2.
 | 
				
			||||||
		return nil, fmt.Errorf("failed to parse user info: %w", err)
 | 
							return nil, fmt.Errorf("failed to parse user info: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if p.userInfoLogging {
 | 
				
			||||||
 | 
							logrus.Tracef("User info from OAuth source %s: %v", p.name, string(contents))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return userFields, nil
 | 
						return userFields, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p PlainOauthAuthenticator) ParseUserInfo(raw map[string]interface{}) (*domain.AuthenticatorUserInfo, error) {
 | 
					func (p PlainOauthAuthenticator) ParseUserInfo(raw map[string]interface{}) (*domain.AuthenticatorUserInfo, error) {
 | 
				
			||||||
	isAdmin, _ := strconv.ParseBool(internal.MapDefaultString(raw, p.userInfoMapping.IsAdmin, ""))
 | 
						return parseOauthUserInfo(p.userInfoMapping, p.userAdminMapping, raw)
 | 
				
			||||||
	userInfo := &domain.AuthenticatorUserInfo{
 | 
					 | 
				
			||||||
		Identifier: domain.UserIdentifier(internal.MapDefaultString(raw, p.userInfoMapping.UserIdentifier, "")),
 | 
					 | 
				
			||||||
		Email:      internal.MapDefaultString(raw, p.userInfoMapping.Email, ""),
 | 
					 | 
				
			||||||
		Firstname:  internal.MapDefaultString(raw, p.userInfoMapping.Firstname, ""),
 | 
					 | 
				
			||||||
		Lastname:   internal.MapDefaultString(raw, p.userInfoMapping.Lastname, ""),
 | 
					 | 
				
			||||||
		Phone:      internal.MapDefaultString(raw, p.userInfoMapping.Phone, ""),
 | 
					 | 
				
			||||||
		Department: internal.MapDefaultString(raw, p.userInfoMapping.Department, ""),
 | 
					 | 
				
			||||||
		IsAdmin:    isAdmin,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return userInfo, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func getOauthFieldMapping(f config.OauthFields) config.OauthFields {
 | 
					 | 
				
			||||||
	defaultMap := config.OauthFields{
 | 
					 | 
				
			||||||
		BaseFields: config.BaseFields{
 | 
					 | 
				
			||||||
			UserIdentifier: "sub",
 | 
					 | 
				
			||||||
			Email:          "email",
 | 
					 | 
				
			||||||
			Firstname:      "given_name",
 | 
					 | 
				
			||||||
			Lastname:       "family_name",
 | 
					 | 
				
			||||||
			Phone:          "phone",
 | 
					 | 
				
			||||||
			Department:     "department",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		IsAdmin: "admin_flag",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f.UserIdentifier != "" {
 | 
					 | 
				
			||||||
		defaultMap.UserIdentifier = f.UserIdentifier
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f.Email != "" {
 | 
					 | 
				
			||||||
		defaultMap.Email = f.Email
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f.Firstname != "" {
 | 
					 | 
				
			||||||
		defaultMap.Firstname = f.Firstname
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f.Lastname != "" {
 | 
					 | 
				
			||||||
		defaultMap.Lastname = f.Lastname
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f.Phone != "" {
 | 
					 | 
				
			||||||
		defaultMap.Phone = f.Phone
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f.Department != "" {
 | 
					 | 
				
			||||||
		defaultMap.Department = f.Department
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f.IsAdmin != "" {
 | 
					 | 
				
			||||||
		defaultMap.IsAdmin = f.IsAdmin
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return defaultMap
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,14 +2,14 @@ package auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/coreos/go-oidc/v3/oidc"
 | 
						"github.com/coreos/go-oidc/v3/oidc"
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal"
 | 
					 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/config"
 | 
						"github.com/h44z/wg-portal/internal/config"
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/domain"
 | 
						"github.com/h44z/wg-portal/internal/domain"
 | 
				
			||||||
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,15 +19,22 @@ type OidcAuthenticator struct {
 | 
				
			||||||
	verifier            *oidc.IDTokenVerifier
 | 
						verifier            *oidc.IDTokenVerifier
 | 
				
			||||||
	cfg                 *oauth2.Config
 | 
						cfg                 *oauth2.Config
 | 
				
			||||||
	userInfoMapping     config.OauthFields
 | 
						userInfoMapping     config.OauthFields
 | 
				
			||||||
 | 
						userAdminMapping    *config.OauthAdminMapping
 | 
				
			||||||
	registrationEnabled bool
 | 
						registrationEnabled bool
 | 
				
			||||||
 | 
						userInfoLogging     bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newOidcAuthenticator(ctx context.Context, callbackUrl string, cfg *config.OpenIDConnectProvider) (*OidcAuthenticator, error) {
 | 
					func newOidcAuthenticator(
 | 
				
			||||||
 | 
						_ context.Context,
 | 
				
			||||||
 | 
						callbackUrl string,
 | 
				
			||||||
 | 
						cfg *config.OpenIDConnectProvider,
 | 
				
			||||||
 | 
					) (*OidcAuthenticator, error) {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var provider = &OidcAuthenticator{}
 | 
						var provider = &OidcAuthenticator{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	provider.name = cfg.ProviderName
 | 
						provider.name = cfg.ProviderName
 | 
				
			||||||
	provider.provider, err = oidc.NewProvider(context.Background(), cfg.BaseUrl) // use new context here, see https://github.com/coreos/go-oidc/issues/339
 | 
						provider.provider, err = oidc.NewProvider(context.Background(),
 | 
				
			||||||
 | 
							cfg.BaseUrl) // use new context here, see https://github.com/coreos/go-oidc/issues/339
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("failed to create new oidc provider: %w", err)
 | 
							return nil, fmt.Errorf("failed to create new oidc provider: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -45,7 +52,9 @@ func newOidcAuthenticator(ctx context.Context, callbackUrl string, cfg *config.O
 | 
				
			||||||
		Scopes:       scopes,
 | 
							Scopes:       scopes,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	provider.userInfoMapping = getOauthFieldMapping(cfg.FieldMap)
 | 
						provider.userInfoMapping = getOauthFieldMapping(cfg.FieldMap)
 | 
				
			||||||
 | 
						provider.userAdminMapping = &cfg.AdminMapping
 | 
				
			||||||
	provider.registrationEnabled = cfg.RegistrationEnabled
 | 
						provider.registrationEnabled = cfg.RegistrationEnabled
 | 
				
			||||||
 | 
						provider.userInfoLogging = cfg.LogUserInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return provider, nil
 | 
						return provider, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -66,11 +75,17 @@ func (o OidcAuthenticator) AuthCodeURL(state string, opts ...oauth2.AuthCodeOpti
 | 
				
			||||||
	return o.cfg.AuthCodeURL(state, opts...)
 | 
						return o.cfg.AuthCodeURL(state, opts...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o OidcAuthenticator) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
 | 
					func (o OidcAuthenticator) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (
 | 
				
			||||||
 | 
						*oauth2.Token,
 | 
				
			||||||
 | 
						error,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
	return o.cfg.Exchange(ctx, code, opts...)
 | 
						return o.cfg.Exchange(ctx, code, opts...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o OidcAuthenticator) GetUserInfo(ctx context.Context, token *oauth2.Token, nonce string) (map[string]interface{}, error) {
 | 
					func (o OidcAuthenticator) GetUserInfo(ctx context.Context, token *oauth2.Token, nonce string) (
 | 
				
			||||||
 | 
						map[string]interface{},
 | 
				
			||||||
 | 
						error,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
	rawIDToken, ok := token.Extra("id_token").(string)
 | 
						rawIDToken, ok := token.Extra("id_token").(string)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return nil, errors.New("token does not contain id_token")
 | 
							return nil, errors.New("token does not contain id_token")
 | 
				
			||||||
| 
						 | 
					@ -88,20 +103,14 @@ func (o OidcAuthenticator) GetUserInfo(ctx context.Context, token *oauth2.Token,
 | 
				
			||||||
		return nil, fmt.Errorf("failed to parse extra claims: %w", err)
 | 
							return nil, fmt.Errorf("failed to parse extra claims: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if o.userInfoLogging {
 | 
				
			||||||
 | 
							contents, _ := json.Marshal(tokenFields)
 | 
				
			||||||
 | 
							logrus.Tracef("User info from OIDC source %s: %v", o.name, string(contents))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return tokenFields, nil
 | 
						return tokenFields, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o OidcAuthenticator) ParseUserInfo(raw map[string]interface{}) (*domain.AuthenticatorUserInfo, error) {
 | 
					func (o OidcAuthenticator) ParseUserInfo(raw map[string]interface{}) (*domain.AuthenticatorUserInfo, error) {
 | 
				
			||||||
	isAdmin, _ := strconv.ParseBool(internal.MapDefaultString(raw, o.userInfoMapping.IsAdmin, ""))
 | 
						return parseOauthUserInfo(o.userInfoMapping, o.userAdminMapping, raw)
 | 
				
			||||||
	userInfo := &domain.AuthenticatorUserInfo{
 | 
					 | 
				
			||||||
		Identifier: domain.UserIdentifier(internal.MapDefaultString(raw, o.userInfoMapping.UserIdentifier, "")),
 | 
					 | 
				
			||||||
		Email:      internal.MapDefaultString(raw, o.userInfoMapping.Email, ""),
 | 
					 | 
				
			||||||
		Firstname:  internal.MapDefaultString(raw, o.userInfoMapping.Firstname, ""),
 | 
					 | 
				
			||||||
		Lastname:   internal.MapDefaultString(raw, o.userInfoMapping.Lastname, ""),
 | 
					 | 
				
			||||||
		Phone:      internal.MapDefaultString(raw, o.userInfoMapping.Phone, ""),
 | 
					 | 
				
			||||||
		Department: internal.MapDefaultString(raw, o.userInfoMapping.Department, ""),
 | 
					 | 
				
			||||||
		IsAdmin:    isAdmin,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return userInfo, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,88 @@
 | 
				
			||||||
 | 
					package auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/h44z/wg-portal/internal"
 | 
				
			||||||
 | 
						"github.com/h44z/wg-portal/internal/config"
 | 
				
			||||||
 | 
						"github.com/h44z/wg-portal/internal/domain"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// parseOauthUserInfo parses the raw user info from the oauth provider and maps it to the internal user info struct
 | 
				
			||||||
 | 
					func parseOauthUserInfo(
 | 
				
			||||||
 | 
						mapping config.OauthFields,
 | 
				
			||||||
 | 
						adminMapping *config.OauthAdminMapping,
 | 
				
			||||||
 | 
						raw map[string]interface{},
 | 
				
			||||||
 | 
					) (*domain.AuthenticatorUserInfo, error) {
 | 
				
			||||||
 | 
						var isAdmin bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// first try to match the is_admin field against the given regex
 | 
				
			||||||
 | 
						if mapping.IsAdmin != "" {
 | 
				
			||||||
 | 
							re := adminMapping.GetAdminValueRegex()
 | 
				
			||||||
 | 
							if re.MatchString(strings.TrimSpace(internal.MapDefaultString(raw, mapping.IsAdmin, ""))) {
 | 
				
			||||||
 | 
								isAdmin = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// next try to parse the user's groups
 | 
				
			||||||
 | 
						if !isAdmin && mapping.UserGroups != "" && adminMapping.AdminGroupRegex != "" {
 | 
				
			||||||
 | 
							userGroups := internal.MapDefaultStringSlice(raw, mapping.UserGroups, nil)
 | 
				
			||||||
 | 
							re := adminMapping.GetAdminGroupRegex()
 | 
				
			||||||
 | 
							for _, group := range userGroups {
 | 
				
			||||||
 | 
								if re.MatchString(strings.TrimSpace(group)) {
 | 
				
			||||||
 | 
									isAdmin = true
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userInfo := &domain.AuthenticatorUserInfo{
 | 
				
			||||||
 | 
							Identifier: domain.UserIdentifier(internal.MapDefaultString(raw, mapping.UserIdentifier, "")),
 | 
				
			||||||
 | 
							Email:      internal.MapDefaultString(raw, mapping.Email, ""),
 | 
				
			||||||
 | 
							Firstname:  internal.MapDefaultString(raw, mapping.Firstname, ""),
 | 
				
			||||||
 | 
							Lastname:   internal.MapDefaultString(raw, mapping.Lastname, ""),
 | 
				
			||||||
 | 
							Phone:      internal.MapDefaultString(raw, mapping.Phone, ""),
 | 
				
			||||||
 | 
							Department: internal.MapDefaultString(raw, mapping.Department, ""),
 | 
				
			||||||
 | 
							IsAdmin:    isAdmin,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return userInfo, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getOauthFieldMapping returns the default field mapping for the oauth provider
 | 
				
			||||||
 | 
					func getOauthFieldMapping(f config.OauthFields) config.OauthFields {
 | 
				
			||||||
 | 
						defaultMap := config.OauthFields{
 | 
				
			||||||
 | 
							BaseFields: config.BaseFields{
 | 
				
			||||||
 | 
								UserIdentifier: "sub",
 | 
				
			||||||
 | 
								Email:          "email",
 | 
				
			||||||
 | 
								Firstname:      "given_name",
 | 
				
			||||||
 | 
								Lastname:       "family_name",
 | 
				
			||||||
 | 
								Phone:          "phone",
 | 
				
			||||||
 | 
								Department:     "department",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							IsAdmin: "admin_flag",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if f.UserIdentifier != "" {
 | 
				
			||||||
 | 
							defaultMap.UserIdentifier = f.UserIdentifier
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if f.Email != "" {
 | 
				
			||||||
 | 
							defaultMap.Email = f.Email
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if f.Firstname != "" {
 | 
				
			||||||
 | 
							defaultMap.Firstname = f.Firstname
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if f.Lastname != "" {
 | 
				
			||||||
 | 
							defaultMap.Lastname = f.Lastname
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if f.Phone != "" {
 | 
				
			||||||
 | 
							defaultMap.Phone = f.Phone
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if f.Department != "" {
 | 
				
			||||||
 | 
							defaultMap.Department = f.Department
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if f.IsAdmin != "" {
 | 
				
			||||||
 | 
							defaultMap.IsAdmin = f.IsAdmin
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return defaultMap
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,11 @@
 | 
				
			||||||
package config
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-ldap/ldap/v3"
 | 
						"github.com/go-ldap/ldap/v3"
 | 
				
			||||||
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Auth struct {
 | 
					type Auth struct {
 | 
				
			||||||
| 
						 | 
					@ -24,6 +26,66 @@ type BaseFields struct {
 | 
				
			||||||
type OauthFields struct {
 | 
					type OauthFields struct {
 | 
				
			||||||
	BaseFields `yaml:",inline"`
 | 
						BaseFields `yaml:",inline"`
 | 
				
			||||||
	IsAdmin    string `yaml:"is_admin"`    // If the value is "true", the user is an admin.
 | 
						IsAdmin    string `yaml:"is_admin"`    // If the value is "true", the user is an admin.
 | 
				
			||||||
 | 
						UserGroups string `yaml:"user_groups"` // This value specifies the claim name that contains the users groups.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OauthAdminMapping contains all necessary information to extract information about administrative privileges
 | 
				
			||||||
 | 
					// from the user info fields.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// WgPortal can grant a user admin rights by matching the value of the `is_admin` claim against a regular expression.
 | 
				
			||||||
 | 
					// Alternatively, a regular expression can be used to check if a user is member of a specific group listed in the
 | 
				
			||||||
 | 
					// `user_group` claim.
 | 
				
			||||||
 | 
					// If one of the cases evaluates to true, the user is granted admin rights.
 | 
				
			||||||
 | 
					type OauthAdminMapping struct {
 | 
				
			||||||
 | 
						// If the regex specified in that field matches the contents of the is_admin field, the user is an admin.
 | 
				
			||||||
 | 
						AdminValueRegex string `yaml:"admin_value_regex"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If any of the groups listed in the groups field matches the group specified in the admin_group_regex field, ]
 | 
				
			||||||
 | 
						// the user is an admin.
 | 
				
			||||||
 | 
						AdminGroupRegex string `yaml:"admin_group_regex"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// internal cache fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						adminValueRegex *regexp.Regexp
 | 
				
			||||||
 | 
						adminGroupRegex *regexp.Regexp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *OauthAdminMapping) GetAdminValueRegex() *regexp.Regexp {
 | 
				
			||||||
 | 
						if o.adminValueRegex != nil {
 | 
				
			||||||
 | 
							return o.adminValueRegex // return cached value
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if o.AdminValueRegex == "" {
 | 
				
			||||||
 | 
							o.adminValueRegex = regexp.MustCompile("^true$") // default value is "true"
 | 
				
			||||||
 | 
							return o.adminValueRegex
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						adminRegex, err := regexp.Compile(o.AdminValueRegex)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logrus.Fatalf("failed to compile admin_value_regex: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						o.adminValueRegex = adminRegex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return o.adminValueRegex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *OauthAdminMapping) GetAdminGroupRegex() *regexp.Regexp {
 | 
				
			||||||
 | 
						if o.adminGroupRegex != nil {
 | 
				
			||||||
 | 
							return o.adminGroupRegex // return cached value
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if o.AdminGroupRegex == "" {
 | 
				
			||||||
 | 
							o.adminGroupRegex = regexp.MustCompile("^wg_portal_default_admin_group$") // default value is "wg_portal_default_admin_group"
 | 
				
			||||||
 | 
							return o.adminGroupRegex
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						groupRegex, err := regexp.Compile(o.AdminGroupRegex)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logrus.Fatalf("failed to compile admin_group_regex: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						o.adminGroupRegex = groupRegex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return o.adminGroupRegex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type LdapFields struct {
 | 
					type LdapFields struct {
 | 
				
			||||||
| 
						 | 
					@ -58,6 +120,9 @@ type LdapProvider struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If RegistrationEnabled is set to true, wg-portal will create new users that do not exist in the database.
 | 
						// If RegistrationEnabled is set to true, wg-portal will create new users that do not exist in the database.
 | 
				
			||||||
	RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
						RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If LogUserInfo is set to true, the user info retrieved from the LDAP provider will be logged in trace level.
 | 
				
			||||||
 | 
						LogUserInfo bool `yaml:"log_user_info"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type OpenIDConnectProvider struct {
 | 
					type OpenIDConnectProvider struct {
 | 
				
			||||||
| 
						 | 
					@ -81,8 +146,15 @@ type OpenIDConnectProvider struct {
 | 
				
			||||||
	// FieldMap is used to map the names of the user-info endpoint fields to wg-portal fields
 | 
						// FieldMap is used to map the names of the user-info endpoint fields to wg-portal fields
 | 
				
			||||||
	FieldMap OauthFields `yaml:"field_map"`
 | 
						FieldMap OauthFields `yaml:"field_map"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// AdminMapping contains all necessary information to extract information about administrative privileges
 | 
				
			||||||
 | 
						// from the user info fields.
 | 
				
			||||||
 | 
						AdminMapping OauthAdminMapping `yaml:"admin_mapping"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If RegistrationEnabled is set to true, missing users will be created in the database
 | 
						// If RegistrationEnabled is set to true, missing users will be created in the database
 | 
				
			||||||
	RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
						RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If LogUserInfo is set to true, the user info retrieved from the OIDC provider will be logged in trace level.
 | 
				
			||||||
 | 
						LogUserInfo bool `yaml:"log_user_info"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type OAuthProvider struct {
 | 
					type OAuthProvider struct {
 | 
				
			||||||
| 
						 | 
					@ -108,6 +180,13 @@ type OAuthProvider struct {
 | 
				
			||||||
	// FieldMap is used to map the names of the user-info endpoint fields to wg-portal fields
 | 
						// FieldMap is used to map the names of the user-info endpoint fields to wg-portal fields
 | 
				
			||||||
	FieldMap OauthFields `yaml:"field_map"`
 | 
						FieldMap OauthFields `yaml:"field_map"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// AdminMapping contains all necessary information to extract information about administrative privileges
 | 
				
			||||||
 | 
						// from the user info fields.
 | 
				
			||||||
 | 
						AdminMapping OauthAdminMapping `yaml:"admin_mapping"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If RegistrationEnabled is set to true, wg-portal will create new users that do not exist in the database.
 | 
						// If RegistrationEnabled is set to true, wg-portal will create new users that do not exist in the database.
 | 
				
			||||||
	RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
						RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If LogUserInfo is set to true, the user info retrieved from the OAuth provider will be logged in trace level.
 | 
				
			||||||
 | 
						LogUserInfo bool `yaml:"log_user_info"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,6 +79,27 @@ func MapDefaultString(m map[string]interface{}, key string, dflt string) string
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MapDefaultStringSlice returns the string slice value for the given key or a default value
 | 
				
			||||||
 | 
					func MapDefaultStringSlice(m map[string]interface{}, key string, dflt []string) []string {
 | 
				
			||||||
 | 
						if m == nil {
 | 
				
			||||||
 | 
							return dflt
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if tmp, ok := m[key]; !ok {
 | 
				
			||||||
 | 
							return dflt
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							switch v := tmp.(type) {
 | 
				
			||||||
 | 
							case []string:
 | 
				
			||||||
 | 
								return v
 | 
				
			||||||
 | 
							case string:
 | 
				
			||||||
 | 
								return []string{v}
 | 
				
			||||||
 | 
							case nil:
 | 
				
			||||||
 | 
								return dflt
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return []string{fmt.Sprintf("%v", v)}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UniqueStringSlice removes duplicates in the given string slice
 | 
					// UniqueStringSlice removes duplicates in the given string slice
 | 
				
			||||||
func UniqueStringSlice(slice []string) []string {
 | 
					func UniqueStringSlice(slice []string) []string {
 | 
				
			||||||
	keys := make(map[string]struct{})
 | 
						keys := make(map[string]struct{})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ theme:
 | 
				
			||||||
  features:
 | 
					  features:
 | 
				
			||||||
    - navigation.instant
 | 
					    - navigation.instant
 | 
				
			||||||
    - navigation.tabs
 | 
					    - navigation.tabs
 | 
				
			||||||
 | 
					    - navigation.expand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
plugins:
 | 
					plugins:
 | 
				
			||||||
  - search
 | 
					  - search
 | 
				
			||||||
| 
						 | 
					@ -61,4 +62,7 @@ nav:
 | 
				
			||||||
          - Building: documentation/getting-started/building.md
 | 
					          - Building: documentation/getting-started/building.md
 | 
				
			||||||
          - Docker Container: documentation/getting-started/docker.md
 | 
					          - Docker Container: documentation/getting-started/docker.md
 | 
				
			||||||
          - Upgrade from V1: documentation/getting-started/upgrade.md
 | 
					          - Upgrade from V1: documentation/getting-started/upgrade.md
 | 
				
			||||||
 | 
					      - Configuration:
 | 
				
			||||||
 | 
					          - Overview: documentation/configuration/overview.md
 | 
				
			||||||
 | 
					          - Examples: documentation/configuration/examples.md
 | 
				
			||||||
      - REST API: documentation/rest-api/api-doc.md
 | 
					      - REST API: documentation/rest-api/api-doc.md
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue