Skip to content

Commit

Permalink
fix: closes #33 change plain user operation
Browse files Browse the repository at this point in the history
  • Loading branch information
doncicuto committed Aug 13, 2022
1 parent d396379 commit 4d2ff4a
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"mode": "auto",
"program": "main.go",
"args": [
"server", "start", "--api-secret", "secret"
"user"
]
}
]
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ $ glim logout
$ glim server stop
```

## Glim user types (roles)

Glim has the following roles:

- Manager. Can create, update and delete users and/or groups and set group memberships.
- Plain users. A plain user can get its user account information, update its name, email, ssh public key and password. A plain user can change its role and its memberships.
- Read-only. Can read users and groups information.

## Secured communications by design

Glim server will listen on 1323 TCP port (REST API) and on 1636 TCP (LDAPS) port and only TLS communications will be allowed in order to secure credentials and data exchange.
Expand Down
20 changes: 19 additions & 1 deletion cmd/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ func AmIManager(token *types.Response) bool {
claims := make(jwt.MapClaims)
jwt.ParseWithClaims(token.AccessToken, claims, nil)

// Extract access token jti
manager, ok := claims["manager"].(bool)
if !ok {
fmt.Printf("Could not parse access token. Please try to log in again\n")
Expand All @@ -157,6 +156,25 @@ func AmIManager(token *types.Response) bool {
return manager
}

// AmIReadonly - TODO comment
func AmIReadonly(token *types.Response) bool {
claims := make(jwt.MapClaims)
jwt.ParseWithClaims(token.AccessToken, claims, nil)

readonly, ok := claims["readonly"].(bool)
if !ok {
fmt.Printf("Could not parse access token. Please try to log in again\n")
os.Exit(1)
}

return readonly
}

// AmIManager - TODO comment
func AmIPlainUser(token *types.Response) bool {
return !AmIManager(token) && !AmIReadonly(token)
}

// WhichIsMyTokenUID - TODO comment
func WhichIsMyTokenUID(token *types.Response) float64 {
claims := make(jwt.MapClaims)
Expand Down
13 changes: 13 additions & 0 deletions cmd/user_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ func GetUserInfo() {
getUser(uid)
os.Exit(0)
}

// Check expiration
token := ReadCredentials()
if NeedsRefresh(token) {
Refresh(token.RefreshToken)
token = ReadCredentials()
}
if AmIPlainUser(token) {
uid = uint(WhichIsMyTokenUID(token))
getUser(uid)
os.Exit(0)
}

getUsers()
}

Expand Down
21 changes: 21 additions & 0 deletions server/api/handlers/user_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ func (h *Handler) UpdateUser(c echo.Context) error {
if !ok {
return &echo.HTTPError{Code: http.StatusNotAcceptable, Message: "wrong token or missing info in token claims"}
}

manager, ok := claims["manager"].(bool)
if !ok {
return &echo.HTTPError{Code: http.StatusNotAcceptable, Message: "wrong token or missing info in token claims"}
}

if err := h.DB.Model(&models.User{}).Where("id = ?", uint(tokenUID)).First(&modifiedBy).Error; err != nil {
return &echo.HTTPError{Code: http.StatusForbidden, Message: "wrong user attempting to update group"}
}
Expand Down Expand Up @@ -122,6 +128,9 @@ func (h *Handler) UpdateUser(c echo.Context) error {
if err != nil {
// Does username exist?
if errors.Is(err, gorm.ErrRecordNotFound) {
if !manager {
return &echo.HTTPError{Code: http.StatusForbidden, Message: "only managers can update the username"}
}
updatedUser["username"] = html.EscapeString(strings.TrimSpace(body.Username))
}
} else {
Expand Down Expand Up @@ -159,14 +168,23 @@ func (h *Handler) UpdateUser(c echo.Context) error {
}

if body.Manager != nil {
if !manager {
return &echo.HTTPError{Code: http.StatusForbidden, Message: "only managers can update manager status"}
}
updatedUser["manager"] = *body.Manager
}

if body.Readonly != nil {
if !manager {
return &echo.HTTPError{Code: http.StatusForbidden, Message: "only managers can update readonly status"}
}
updatedUser["readonly"] = *body.Readonly
}

if body.Locked != nil {
if !manager {
return &echo.HTTPError{Code: http.StatusForbidden, Message: "only managers can update locked status"}
}
updatedUser["locked"] = *body.Locked
}

Expand Down Expand Up @@ -196,6 +214,9 @@ func (h *Handler) UpdateUser(c echo.Context) error {

// Update group members
if body.MemberOf != "" {
if !manager {
return &echo.HTTPError{Code: http.StatusForbidden, Message: "only managers can update group memberships"}
}
members := strings.Split(body.MemberOf, ",")

if body.ReplaceMembersOf {
Expand Down
50 changes: 50 additions & 0 deletions server/api/middleware/updater.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright © 2022 Miguel Ángel Álvarez Cabrerizo <[email protected]>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package middleware

import (
"fmt"
"net/http"

"github.com/golang-jwt/jwt"
"github.com/labstack/echo/v4"
)

//IsReader - TODO comment
func IsUpdater(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
uid := c.Param("uid")
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
jwtID, ok := claims["uid"].(float64)
if !ok {
return &echo.HTTPError{Code: http.StatusNotAcceptable, Message: "wrong token or missing info in token claims"}
}
jwtReadonly, ok := claims["readonly"].(bool)
if !ok {
return &echo.HTTPError{Code: http.StatusNotAcceptable, Message: "wrong token or missing info in token claims"}
}
jwtManager, ok := claims["manager"].(bool)
if !ok {
return &echo.HTTPError{Code: http.StatusNotAcceptable, Message: "wrong token or missing info in token claims"}
}
if !jwtReadonly && (jwtManager || uid == fmt.Sprintf("%d", uint(jwtID))) {
return next(c)
}
return &echo.HTTPError{Code: http.StatusForbidden, Message: "user has no proper permissions"}
}
}
2 changes: 1 addition & 1 deletion server/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func Server(wg *sync.WaitGroup, shutdownChannel chan bool, settings types.APISet
u.POST("", h.SaveUser, glimMiddleware.IsBlacklisted(blacklist), glimMiddleware.IsManager)
u.GET("/:uid", h.FindUserByID, glimMiddleware.IsBlacklisted(blacklist), glimMiddleware.IsReader)
u.GET("/:username/uid", h.FindUIDFromUsername, glimMiddleware.IsBlacklisted(blacklist), glimMiddleware.IsReader)
u.PUT("/:uid", h.UpdateUser, glimMiddleware.IsBlacklisted(blacklist), glimMiddleware.IsManager)
u.PUT("/:uid", h.UpdateUser, glimMiddleware.IsBlacklisted(blacklist), glimMiddleware.IsUpdater)
u.DELETE("/:uid", h.DeleteUser, glimMiddleware.IsBlacklisted(blacklist), glimMiddleware.IsManager)
u.POST("/:uid/passwd", h.Passwd, glimMiddleware.IsBlacklisted(blacklist))

Expand Down

0 comments on commit 4d2ff4a

Please sign in to comment.