Compare commits

..

2 Commits

Author SHA1 Message Date
htiryaki-oe24
0a8ec71b3f Fixes minor chart issues #629 (#630)
(cherry picked from commit 3e0ffec07c)
2026-02-24 22:41:28 +01:00
h44z
fe4485037a Merge commit from fork 2026-02-24 22:40:13 +01:00
10 changed files with 104 additions and 6 deletions

View File

@@ -16,7 +16,7 @@ annotations:
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.7.2
version: 0.7.3
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to

View File

@@ -1,6 +1,6 @@
# wg-portal
![Version: 0.7.2](https://img.shields.io/badge/Version-0.7.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v2](https://img.shields.io/badge/AppVersion-v2-informational?style=flat-square)
![Version: 0.7.3](https://img.shields.io/badge/Version-0.7.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v2](https://img.shields.io/badge/AppVersion-v2-informational?style=flat-square)
WireGuard Configuration Portal with LDAP, OAuth, OIDC authentication
@@ -41,6 +41,7 @@ The [Values](#values) section lists the parameters that can be configured during
| config.web | tpl/object | `{}` | [Web configuration](https://wgportal.org/latest/documentation/configuration/overview/#web) options.<br> `listening_address` will be set automatically from `service.web.port`. `external_url` is required to enable ingress and certificate resources. |
| revisionHistoryLimit | string | `10` | The number of old ReplicaSets to retain to allow rollback. |
| workloadType | string | `"Deployment"` | Workload type - `Deployment` or `StatefulSet` |
| replicas | int | `1` | The replicas for the workload. |
| strategy | object | `{"type":"RollingUpdate"}` | Update strategy for the workload Valid values are: `RollingUpdate` or `Recreate` for Deployment, `RollingUpdate` or `OnDelete` for StatefulSet |
| image.repository | string | `"ghcr.io/h44z/wg-portal"` | Image repository |
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
@@ -74,12 +75,15 @@ The [Values](#values) section lists the parameters that can be configured during
| service.web.type | string | `"ClusterIP"` | Web service type |
| service.web.port | int | `8888` | Web service port Used for the web interface listener |
| service.web.appProtocol | string | `"http"` | Web service appProtocol. Will be auto set to `https` if certificate is enabled. |
| service.web.extraSelectorLabels | object | `{}` | Extra labels to append to the selector labels. |
| service.wireguard.annotations | object | `{}` | Annotations for the WireGuard service |
| service.wireguard.type | string | `"LoadBalancer"` | Wireguard service type |
| service.wireguard.ports | list | `[51820]` | Wireguard service ports. Exposes the WireGuard ports for created interfaces. Lowerest port is selected as start port for the first interface. Increment next port by 1 for each additional interface. |
| service.wireguard.extraSelectorLabels | object | `{}` | Extra labels to append to the selector labels. |
| service.metrics.port | int | `8787` | |
| ingress.enabled | bool | `false` | Specifies whether an ingress resource should be created |
| ingress.className | string | `""` | Ingress class name |
| ingress.pathType | string | `"ImplementationSpecific"` | Ingress pathType value. Valid values are `ImplementationSpecific`, `Exact` or `Prefix`. |
| ingress.annotations | object | `{}` | Ingress annotations |
| ingress.tls | bool | `false` | Ingress TLS configuration. Enable certificate resource or add ingress annotation to create required secret |
| certificate.enabled | bool | `false` | Specifies whether a certificate resource should be created. If enabled, certificate will be used for the web. |

View File

@@ -49,7 +49,7 @@ spec:
{{- with .scope.type }}
type: {{ . }}
{{- end }}
selector: {{- include "wg-portal.selectorLabels" .context | nindent 4 }}
selector: {{- include "wg-portal.util.merge" (list .context .scope.extraSelectorLabels "wg-portal.selectorLabels") | nindent 4 }}
{{- end -}}
{{/*

View File

@@ -8,6 +8,9 @@ spec:
{{- with .Values.revisionHistoryLimit }}
revisionHistoryLimit: {{ . }}
{{- end }}
{{- with .Values.replicas }}
replicas: {{ . }}
{{- end }}
{{- with .Values.strategy }}
strategy: {{- toYaml . | nindent 4 }}
{{- end }}

View File

@@ -15,7 +15,7 @@ spec:
http:
paths:
- path: {{ default "/" (urlParse (tpl .Values.config.web.external_url .)).path }}
pathType: {{ default "ImplementationSpecific" .pathType }}
pathType: {{ default "ImplementationSpecific" .Values.ingress.pathType }}
backend:
service:
name: {{ include "wg-portal.fullname" . }}

View File

@@ -8,6 +8,9 @@ spec:
{{- with .Values.revisionHistoryLimit }}
revisionHistoryLimit: {{ . }}
{{- end }}
{{- with .Values.replicas }}
replicas: {{ . }}
{{- end }}
{{- with .Values.strategy }}
updateStrategy: {{- toYaml . | nindent 4 }}
{{- end }}

View File

@@ -35,6 +35,9 @@ config:
revisionHistoryLimit: ""
# -- Workload type - `Deployment` or `StatefulSet`
workloadType: Deployment
# -- The replicas for the workload.
# @default -- `1`
replicas: 1
# -- Update strategy for the workload
# Valid values are:
# `RollingUpdate` or `Recreate` for Deployment,
@@ -124,6 +127,8 @@ service:
port: 8888
# -- Web service appProtocol. Will be auto set to `https` if certificate is enabled.
appProtocol: http
# -- Extra labels to append to the selector labels.
extraSelectorLabels: {}
wireguard:
# -- Annotations for the WireGuard service
annotations: {}
@@ -135,6 +140,8 @@ service:
# Increment next port by 1 for each additional interface.
ports:
- 51820
# -- Extra labels to append to the selector labels.
extraSelectorLabels: {}
metrics:
port: 8787
@@ -143,6 +150,10 @@ ingress:
enabled: false
# -- Ingress class name
className: ""
# -- Ingress pathType value.
# Valid values are `ImplementationSpecific`, `Exact` or `Prefix`.
# @default -- `"ImplementationSpecific"`
pathType: "ImplementationSpecific"
# -- Ingress annotations
annotations: {}
# -- Ingress TLS configuration.

View File

@@ -53,6 +53,17 @@ func (u UserService) GetAllUsers(ctx context.Context) ([]domain.User, error) {
}
func (u UserService) UpdateUser(ctx context.Context, user *domain.User) (*domain.User, error) {
sessionUser := domain.GetUserInfo(ctx)
currentUser, err := u.users.GetUser(ctx, user.Identifier)
if err != nil {
return nil, err
}
// if this endpoint is used by non-admins, make sure that the user can only modify a specific subset of attributes
if !sessionUser.IsAdmin {
user.CopyAdminAttributes(currentUser, u.cfg.Advanced.ApiAdminOnly)
}
return u.users.UpdateUser(ctx, user)
}

View File

@@ -352,8 +352,9 @@ func (m Manager) DeactivateApi(ctx context.Context, id domain.UserIdentifier) (*
func (m Manager) validateModifications(ctx context.Context, old, new *domain.User) error {
currentUser := domain.GetUserInfo(ctx)
if currentUser.Id != new.Identifier && !currentUser.IsAdmin {
return fmt.Errorf("insufficient permissions")
adminErrors := m.validateAdminModifications(ctx, old, new)
if adminErrors != nil {
return adminErrors
}
if err := old.EditAllowed(new); err != nil && currentUser.Id != domain.SystemAdminContextUserInfo().Id {
@@ -387,6 +388,42 @@ func (m Manager) validateModifications(ctx context.Context, old, new *domain.Use
return nil
}
func (m Manager) validateAdminModifications(ctx context.Context, old, new *domain.User) error {
currentUser := domain.GetUserInfo(ctx)
if currentUser.IsAdmin {
if currentUser.Id == old.Identifier && !new.IsAdmin {
return fmt.Errorf("cannot remove own admin rights: %w", domain.ErrInvalidData)
}
return nil // admins can do (almost) everything
}
// non-admins can only modify very their own profile data
if currentUser.Id != new.Identifier {
return fmt.Errorf("insufficient permissions: %w", domain.ErrInvalidData)
}
if new.IsAdmin {
return fmt.Errorf("cannot grant admin rights: %w", domain.ErrInvalidData)
}
if new.Notes != old.Notes {
return fmt.Errorf("cannot update notes: %w", domain.ErrInvalidData)
}
if old.Locked != new.Locked || old.LockedReason != new.LockedReason {
return fmt.Errorf("cannot change lock state: %w", domain.ErrInvalidData)
}
if old.Disabled != new.Disabled || old.DisabledReason != new.DisabledReason {
return fmt.Errorf("cannot change disabled state: %w", domain.ErrInvalidData)
}
return nil
}
func (m Manager) validateCreation(ctx context.Context, new *domain.User) error {
currentUser := domain.GetUserInfo(ctx)
@@ -453,6 +490,10 @@ func (m Manager) validateDeletion(ctx context.Context, del *domain.User) error {
func (m Manager) validateApiChange(ctx context.Context, user *domain.User) error {
currentUser := domain.GetUserInfo(ctx)
if !currentUser.IsAdmin && m.cfg.Advanced.ApiAdminOnly {
return fmt.Errorf("insufficient permissions to change API access: %w", domain.ErrNoPermission)
}
if currentUser.Id != user.Identifier {
return fmt.Errorf("cannot change API access of user: %w", domain.ErrNoPermission)
}

View File

@@ -185,6 +185,31 @@ func (u *User) CopyCalculatedAttributes(src *User) {
u.LinkedPeerCount = src.LinkedPeerCount
}
// CopyAdminAttributes copies all attributes from the given user except password, passkey and
// api-token if apiAdminOnly is false.
func (u *User) CopyAdminAttributes(src *User, apiAdminOnly bool) {
u.BaseModel = src.BaseModel
u.Identifier = src.Identifier
u.Email = src.Email
u.Source = src.Source
u.ProviderName = src.ProviderName
u.IsAdmin = src.IsAdmin
u.Firstname = src.Firstname
u.Lastname = src.Lastname
u.Phone = src.Phone
u.Department = src.Department
u.Notes = src.Notes
u.Disabled = src.Disabled
u.DisabledReason = src.DisabledReason
u.Locked = src.Locked
u.LockedReason = src.LockedReason
u.LinkedPeerCount = src.LinkedPeerCount
if apiAdminOnly {
u.ApiToken = src.ApiToken
u.ApiTokenCreated = src.ApiTokenCreated
}
}
// DisplayName returns the display name of the user.
// The display name is the first and last name, or the email address of the user.
// If none of these fields are set, the user identifier is returned.