feat: allow regular users to login to the UI
This commit is contained in:
parent
e0308acef3
commit
6978790e96
|
@ -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...)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue