diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 1565fc30..eb36148a 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -26,6 +26,8 @@ const ( // UserKey is the key on which the User profile is set on echo handlers. UserKey = "auth_user" SessionKey = "auth_session" + + SuperAdminRole = 1 ) const ( @@ -236,6 +238,22 @@ func (o *Auth) Middleware(next echo.HandlerFunc) echo.HandlerFunc { func (o *Auth) Perm(next echo.HandlerFunc, perm string) echo.HandlerFunc { return func(c echo.Context) error { + u, ok := c.Get(UserKey).(models.User) + if !ok { + c.Set(UserKey, echo.NewHTTPError(http.StatusForbidden, "invalid session")) + return next(c) + } + + // If there's no permission set on the handler or if the current user is a super admin, do no checks. + if perm == "" || u.RoleID == SuperAdminRole { + return next(c) + } + + // Check if the current handler's permission is in the user's permission map. + if _, ok := u.PermissionsMap[perm]; !ok { + return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("permission denied (%s)", perm)) + } + return next(c) } } diff --git a/internal/core/users.go b/internal/core/users.go index f0946ff5..e6af6f2e 100644 --- a/internal/core/users.go +++ b/internal/core/users.go @@ -48,6 +48,11 @@ func (c *Core) GetUser(id int, username, email string) (models.User, error) { out.PasswordLogin = true } + out.PermissionsMap = make(map[string]struct{}) + for _, p := range out.Permissions { + out.PermissionsMap[p] = struct{}{} + } + return out, nil } @@ -141,5 +146,10 @@ func (c *Core) LoginUser(username, password string) (models.User, error) { c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.users}", "error", pqErrMsg(err))) } + out.PermissionsMap = make(map[string]struct{}) + for _, p := range out.Permissions { + out.PermissionsMap[p] = struct{}{} + } + return out, nil } diff --git a/models/models.go b/models/models.go index 7ae3116d..d9969127 100644 --- a/models/models.go +++ b/models/models.go @@ -162,7 +162,7 @@ type User struct { Status string `db:"status" json:"status"` Avatar null.String `db:"-" json:"avatar"` - PermissionsTbl map[string]struct{} `db:"-" json:"-"` + PermissionsMap map[string]struct{} `db:"-" json:"-"` LoggedInAt null.Time `db:"loggedin_at" json:"loggedin_at"` HasPassword bool `db:"-" json:"-"` diff --git a/queries.sql b/queries.sql index c9b17aba..8ab5058e 100644 --- a/queries.sql +++ b/queries.sql @@ -1084,7 +1084,9 @@ SELECT username, password FROM users WHERE status='enabled' AND type='api'; -- name: login-user WITH u AS ( - SELECT * FROM users WHERE username=$1 AND status != 'disabled' AND password_login = TRUE + SELECT users.*, r.name as role_name, r.permissions FROM users + LEFT JOIN user_roles r ON (r.id = users.role_id) + WHERE username=$1 AND status != 'disabled' AND password_login = TRUE ) SELECT * FROM u WHERE CRYPT($2, password) = password;