feat: allow regular users to login to the UI

This commit is contained in:
Deluan 2020-02-05 22:22:44 -05:00
parent e0308acef3
commit 6978790e96
5 changed files with 57 additions and 21 deletions

View File

@ -31,6 +31,14 @@ func userId(ctx context.Context) string {
return usr.ID 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 { func (r sqlRepository) newSelect(options ...model.QueryOptions) SelectBuilder {
sq := Select().From(r.tableName) sq := Select().From(r.tableName)
sq = r.applyOptions(sq, options...) sq = r.applyOptions(sq, options...)

View File

@ -85,10 +85,18 @@ func (r *userRepository) UpdateLastAccessAt(id string) error {
} }
func (r *userRepository) Count(options ...rest.QueryOptions) (int64, 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...)) return r.CountAll(r.parseRestOptions(options...))
} }
func (r *userRepository) Read(id string) (interface{}, error) { 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) usr, err := r.Get(id)
if err == model.ErrNotFound { if err == model.ErrNotFound {
return nil, rest.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) { 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...)) return r.GetAll(r.parseRestOptions(options...))
} }
@ -109,17 +121,25 @@ func (r *userRepository) NewInstance() interface{} {
} }
func (r *userRepository) Save(entity interface{}) (string, error) { func (r *userRepository) Save(entity interface{}) (string, error) {
usr := entity.(*model.User) usr := loggedUser(r.ctx)
err := r.Put(usr) if !usr.IsAdmin {
return "", rest.ErrPermissionDenied
}
u := entity.(*model.User)
err := r.Put(u)
if err != nil { if err != nil {
return "", err return "", err
} }
return usr.ID, err return u.ID, err
} }
func (r *userRepository) Update(entity interface{}, cols ...string) error { func (r *userRepository) Update(entity interface{}, cols ...string) error {
usr := entity.(*model.User) u := entity.(*model.User)
err := r.Put(usr) usr := loggedUser(r.ctx)
if !usr.IsAdmin && usr.ID != u.ID {
return rest.ErrPermissionDenied
}
err := r.Put(u)
if err == model.ErrNotFound { if err == model.ErrNotFound {
return rest.ErrNotFound return rest.ErrNotFound
} }
@ -127,7 +147,11 @@ func (r *userRepository) Update(entity interface{}, cols ...string) error {
} }
func (r *userRepository) Delete(id 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 { if err == model.ErrNotFound {
return rest.ErrNotFound return rest.ErrNotFound
} }

View File

@ -63,6 +63,7 @@ func handleLogin(ds model.DataStore, username string, password string, w http.Re
"token": tokenString, "token": tokenString,
"name": user.Name, "name": user.Name,
"username": username, "username": username,
"isAdmin": user.IsAdmin,
}) })
} }
@ -148,10 +149,6 @@ func validateLogin(userRepo model.UserRepository, userName, password string) (*m
if u.Password != password { if u.Password != password {
return nil, nil return nil, nil
} }
if !u.IsAdmin {
log.Warn("Non-admin user tried to login", "user", userName)
return nil, nil
}
err = userRepo.UpdateLastLoginAt(u.ID) err = userRepo.UpdateLastLoginAt(u.ID)
if err != nil { if err != nil {
log.Error("Could not update LastLoginAt", "user", userName) 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 := token.Claims.(jwt.MapClaims)
claims["iss"] = consts.JWTIssuer claims["iss"] = consts.JWTIssuer
claims["sub"] = u.UserName claims["sub"] = u.UserName
claims["adm"] = u.IsAdmin
return touchToken(token) return touchToken(token)
} }
@ -176,11 +174,10 @@ func touchToken(token *jwt.Token) (string, error) {
return token.SignedString(jwtSecret) return token.SignedString(jwtSecret)
} }
func userFrom(claims jwt.MapClaims) *model.User { func contextWithUser(ctx context.Context, ds model.DataStore, claims jwt.MapClaims) context.Context {
user := &model.User{ userName := claims["sub"].(string)
UserName: claims["sub"].(string), user, _ := ds.User(ctx).FindByUsername(userName)
} return context.WithValue(ctx, "user", user)
return user
} }
func getToken(ds model.DataStore, ctx context.Context) (*jwt.Token, error) { 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) claims := token.Claims.(jwt.MapClaims)
newCtx := context.WithValue(r.Context(), "loggedUser", userFrom(claims)) newCtx := contextWithUser(r.Context(), ds, claims)
newTokenString, err := touchToken(token) newTokenString, err := touchToken(token)
if err != nil { if err != nil {
log.Error(r, "signing new token", err) log.Error(r, "signing new token", err)

View File

@ -20,10 +20,12 @@ const App = () => (
layout={Layout} layout={Layout}
loginPage={Login} loginPage={Login}
> >
<Resource name="artist" {...artist} options={{ subMenu: 'library' }} /> {(permissions) => [
<Resource name="album" {...album} options={{ subMenu: 'library' }} /> <Resource name="artist" {...artist} options={{ subMenu: 'library' }} />,
<Resource name="song" {...song} options={{ subMenu: 'library' }} /> <Resource name="album" {...album} options={{ subMenu: 'library' }} />,
<Resource name="user" {...user} /> <Resource name="song" {...song} options={{ subMenu: 'library' }} />,
permissions === 'admin' ? <Resource name="user" {...user} /> : null
]}
</Admin> </Admin>
) )
export default App export default App

View File

@ -25,6 +25,7 @@ const authProvider = {
localStorage.setItem('token', response.token) localStorage.setItem('token', response.token)
localStorage.setItem('name', response.name) localStorage.setItem('name', response.name)
localStorage.setItem('username', response.username) localStorage.setItem('username', response.username)
localStorage.setItem('role', response.isAdmin ? 'admin' : 'regular')
return response return response
}) })
.catch((error) => { .catch((error) => {
@ -59,13 +60,17 @@ const authProvider = {
return Promise.resolve() return Promise.resolve()
}, },
getPermissions: (params) => Promise.resolve() getPermissions: () => {
const role = localStorage.getItem('role')
return role ? Promise.resolve(role) : Promise.reject()
}
} }
const removeItems = () => { const removeItems = () => {
localStorage.removeItem('token') localStorage.removeItem('token')
localStorage.removeItem('name') localStorage.removeItem('name')
localStorage.removeItem('username') localStorage.removeItem('username')
localStorage.removeItem('role')
} }
export default authProvider export default authProvider