diff --git a/persistence/sql_base_repository.go b/persistence/sql_base_repository.go index fb2a2cfe..e14c7a26 100644 --- a/persistence/sql_base_repository.go +++ b/persistence/sql_base_repository.go @@ -31,6 +31,14 @@ func userId(ctx context.Context) string { return usr.ID } +func loggedUser(ctx context.Context) *model.User { + user := ctx.Value("user") + if user == nil { + return &model.User{} + } + return user.(*model.User) +} + func (r sqlRepository) newSelect(options ...model.QueryOptions) SelectBuilder { sq := Select().From(r.tableName) sq = r.applyOptions(sq, options...) diff --git a/persistence/user_repository.go b/persistence/user_repository.go index 8f68d763..fbb3a63a 100644 --- a/persistence/user_repository.go +++ b/persistence/user_repository.go @@ -85,10 +85,18 @@ func (r *userRepository) UpdateLastAccessAt(id string) error { } func (r *userRepository) Count(options ...rest.QueryOptions) (int64, error) { + usr := loggedUser(r.ctx) + if !usr.IsAdmin { + return 0, rest.ErrPermissionDenied + } return r.CountAll(r.parseRestOptions(options...)) } func (r *userRepository) Read(id string) (interface{}, error) { + usr := loggedUser(r.ctx) + if !usr.IsAdmin && usr.ID != id { + return nil, rest.ErrPermissionDenied + } usr, err := r.Get(id) if err == model.ErrNotFound { return nil, rest.ErrNotFound @@ -97,6 +105,10 @@ func (r *userRepository) Read(id string) (interface{}, error) { } func (r *userRepository) ReadAll(options ...rest.QueryOptions) (interface{}, error) { + usr := loggedUser(r.ctx) + if !usr.IsAdmin { + return nil, rest.ErrPermissionDenied + } return r.GetAll(r.parseRestOptions(options...)) } @@ -109,17 +121,25 @@ func (r *userRepository) NewInstance() interface{} { } func (r *userRepository) Save(entity interface{}) (string, error) { - usr := entity.(*model.User) - err := r.Put(usr) + usr := loggedUser(r.ctx) + if !usr.IsAdmin { + return "", rest.ErrPermissionDenied + } + u := entity.(*model.User) + err := r.Put(u) if err != nil { return "", err } - return usr.ID, err + return u.ID, err } func (r *userRepository) Update(entity interface{}, cols ...string) error { - usr := entity.(*model.User) - err := r.Put(usr) + u := entity.(*model.User) + usr := loggedUser(r.ctx) + if !usr.IsAdmin && usr.ID != u.ID { + return rest.ErrPermissionDenied + } + err := r.Put(u) if err == model.ErrNotFound { return rest.ErrNotFound } @@ -127,7 +147,11 @@ func (r *userRepository) Update(entity interface{}, cols ...string) error { } func (r *userRepository) Delete(id string) error { - err := r.Delete(id) + usr := loggedUser(r.ctx) + if !usr.IsAdmin && usr.ID != id { + return rest.ErrPermissionDenied + } + err := r.delete(Eq{"id": id}) if err == model.ErrNotFound { return rest.ErrNotFound } diff --git a/server/app/auth.go b/server/app/auth.go index c837502d..49a06948 100644 --- a/server/app/auth.go +++ b/server/app/auth.go @@ -63,6 +63,7 @@ func handleLogin(ds model.DataStore, username string, password string, w http.Re "token": tokenString, "name": user.Name, "username": username, + "isAdmin": user.IsAdmin, }) } @@ -148,10 +149,6 @@ func validateLogin(userRepo model.UserRepository, userName, password string) (*m if u.Password != password { return nil, nil } - if !u.IsAdmin { - log.Warn("Non-admin user tried to login", "user", userName) - return nil, nil - } err = userRepo.UpdateLastLoginAt(u.ID) if err != nil { log.Error("Could not update LastLoginAt", "user", userName) @@ -164,6 +161,7 @@ func createToken(u *model.User) (string, error) { claims := token.Claims.(jwt.MapClaims) claims["iss"] = consts.JWTIssuer claims["sub"] = u.UserName + claims["adm"] = u.IsAdmin return touchToken(token) } @@ -176,11 +174,10 @@ func touchToken(token *jwt.Token) (string, error) { return token.SignedString(jwtSecret) } -func userFrom(claims jwt.MapClaims) *model.User { - user := &model.User{ - UserName: claims["sub"].(string), - } - return user +func contextWithUser(ctx context.Context, ds model.DataStore, claims jwt.MapClaims) context.Context { + userName := claims["sub"].(string) + user, _ := ds.User(ctx).FindByUsername(userName) + return context.WithValue(ctx, "user", user) } func getToken(ds model.DataStore, ctx context.Context) (*jwt.Token, error) { @@ -217,7 +214,7 @@ func Authenticator(ds model.DataStore) func(next http.Handler) http.Handler { claims := token.Claims.(jwt.MapClaims) - newCtx := context.WithValue(r.Context(), "loggedUser", userFrom(claims)) + newCtx := contextWithUser(r.Context(), ds, claims) newTokenString, err := touchToken(token) if err != nil { log.Error(r, "signing new token", err) diff --git a/ui/src/App.js b/ui/src/App.js index 71116e1e..2448597a 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -20,10 +20,12 @@ const App = () => ( layout={Layout} loginPage={Login} > - - - - + {(permissions) => [ + , + , + , + permissions === 'admin' ? : null + ]} ) export default App diff --git a/ui/src/authProvider.js b/ui/src/authProvider.js index 5492aa7a..3ee09b35 100644 --- a/ui/src/authProvider.js +++ b/ui/src/authProvider.js @@ -25,6 +25,7 @@ const authProvider = { localStorage.setItem('token', response.token) localStorage.setItem('name', response.name) localStorage.setItem('username', response.username) + localStorage.setItem('role', response.isAdmin ? 'admin' : 'regular') return response }) .catch((error) => { @@ -59,13 +60,17 @@ const authProvider = { return Promise.resolve() }, - getPermissions: (params) => Promise.resolve() + getPermissions: () => { + const role = localStorage.getItem('role') + return role ? Promise.resolve(role) : Promise.reject() + } } const removeItems = () => { localStorage.removeItem('token') localStorage.removeItem('name') localStorage.removeItem('username') + localStorage.removeItem('role') } export default authProvider