Allow regular users to change their info, including password.

Should fix #199
This commit is contained in:
Deluan 2021-04-28 22:35:25 -04:00
parent 22582392a0
commit a35de2bfd1
9 changed files with 77 additions and 25 deletions

View File

@ -37,6 +37,7 @@ func CreateToken(u *model.User) (string, error) {
claims := token.Claims.(jwt.MapClaims)
claims["iss"] = consts.JWTIssuer
claims["sub"] = u.UserName
claims["uid"] = u.ID
claims["adm"] = u.IsAdmin
return TouchToken(token)

View File

@ -144,6 +144,10 @@ func (r *userRepository) Update(entity interface{}, cols ...string) error {
if !usr.IsAdmin && usr.ID != u.ID {
return rest.ErrPermissionDenied
}
if !usr.IsAdmin {
u.IsAdmin = false
u.UserName = usr.UserName
}
err := r.Put(u)
if err == model.ErrNotFound {
return rest.ErrNotFound
@ -153,7 +157,7 @@ func (r *userRepository) Update(entity interface{}, cols ...string) error {
func (r *userRepository) Delete(id string) error {
usr := loggedUser(r.ctx)
if !usr.IsAdmin && usr.ID != id {
if !usr.IsAdmin {
return rest.ErrPermissionDenied
}
err := r.delete(Eq{"id": id})

View File

@ -87,6 +87,9 @@
"name": "Nome",
"password": "Senha",
"createdAt": "Data de Criação"
},
"helperTexts": {
"name": "Alterações no seu nome só serão refletidas no próximo login"
}
},
"player": {
@ -348,4 +351,4 @@
"toggle_love": "Marcar/desmarcar favorita"
}
}
}
}

View File

@ -60,6 +60,7 @@ func handleLogin(ds model.DataStore, username string, password string, w http.Re
payload := map[string]interface{}{
"message": "User '" + username + "' authenticated successfully",
"token": tokenString,
"id": user.ID,
"name": user.Name,
"username": username,
"isAdmin": user.IsAdmin,

View File

@ -93,9 +93,7 @@ const Admin = (props) => {
{...playlist}
options={{ subMenu: 'library' }}
/>,
permissions === 'admin' ? (
<Resource name="user" {...user} options={{ subMenu: 'settings' }} />
) : null,
<Resource name="user" {...user} options={{ subMenu: 'settings' }} />,
<Resource
name="player"
{...player}

View File

@ -26,7 +26,9 @@ const authProvider = {
.then((response) => {
// Validate token
jwtDecode(response.token)
// TODO Store all items in one object
localStorage.setItem('token', response.token)
localStorage.setItem('userId', response.id)
localStorage.setItem('name', response.name)
localStorage.setItem('username', response.username)
response.avatar && localStorage.setItem('avatar', response.avatar)
@ -94,6 +96,7 @@ const authProvider = {
const removeItems = () => {
localStorage.removeItem('token')
localStorage.removeItem('userId')
localStorage.removeItem('name')
localStorage.removeItem('username')
localStorage.removeItem('avatar')

View File

@ -87,6 +87,9 @@
"name": "Name",
"password": "Password",
"createdAt": "Created at"
},
"helperTexts": {
"name": "Changes to your name will only be reflected on next login"
}
},
"player": {

View File

@ -58,6 +58,7 @@ const AboutMenuItem = forwardRef(({ onClick, ...rest }, ref) => {
})
const settingsResources = (resource) =>
resource.name !== 'user' &&
resource.hasList &&
resource.options &&
resource.options.subMenu === 'settings'
@ -68,16 +69,31 @@ const CustomUserMenu = ({ onClick, ...rest }) => {
const classes = useStyles(rest)
const { permissions } = usePermissions()
const renderSettingsMenuItemLink = (resource) => {
const resourceDefinition = (resourceName) =>
resources.find((r) => r?.name === resourceName)
const renderUserMenuItemLink = () => {
const userResource = resourceDefinition('user')
if (!userResource) {
return null
}
return renderSettingsMenuItemLink(
userResource,
permissions !== 'admin' ? localStorage.getItem('userId') : null
)
}
const renderSettingsMenuItemLink = (resource, id) => {
const label = translate(`resources.${resource.name}.name`, {
smart_count: 2,
smart_count: id ? 1 : 2,
})
const link = id ? `/${resource.name}/${id}` : `/${resource.name}`
return (
<MenuItemLink
className={classes.root}
activeClassName={classes.active}
key={resource.name}
to={`/${resource.name}`}
to={link}
primaryText={label}
leftIcon={
(resource.icon && createElement(resource.icon)) || <ViewListIcon />
@ -94,6 +110,7 @@ const CustomUserMenu = ({ onClick, ...rest }) => {
<UserMenu {...rest}>
<PersonalMenu sidebarIsOpen={true} onClick={onClick} />
<Divider />
{renderUserMenuItemLink()}
{resources.filter(settingsResources).map(renderSettingsMenuItemLink)}
<Divider />
<AboutMenuItem />

View File

@ -31,25 +31,47 @@ const UserTitle = ({ record }) => {
const UserToolbar = (props) => (
<Toolbar {...props} classes={useStyles()}>
<SaveButton />
<DeleteUserButton />
<SaveButton disabled={props.pristine} />
{props.permissions === 'admin' && <DeleteUserButton />}
</Toolbar>
)
const UserEdit = (props) => (
<Edit title={<UserTitle />} {...props}>
<SimpleForm variant={'outlined'} toolbar={<UserToolbar />}>
<TextInput source="userName" validate={[required()]} />
<TextInput source="name" validate={[required()]} />
<TextInput source="email" validate={[email()]} />
<PasswordInput source="password" validate={[required()]} />
<BooleanInput source="isAdmin" initialValue={false} />
<DateField variant="body1" source="lastLoginAt" showTime />
{/*<DateField source="lastAccessAt" showTime />*/}
<DateField variant="body1" source="updatedAt" showTime />
<DateField variant="body1" source="createdAt" showTime />
</SimpleForm>
</Edit>
)
const UserEdit = (props) => {
const { permissions } = props
const translate = useTranslate()
const getNameHelperText = () =>
props.id === localStorage.getItem('userId') && {
helperText: translate('resources.user.helperTexts.name'),
}
return (
<Edit title={<UserTitle />} {...props}>
<SimpleForm
variant={'outlined'}
toolbar={<UserToolbar />}
redirect={permissions === 'admin' ? 'list' : false}
>
{permissions === 'admin' && (
<TextInput source="userName" validate={[required()]} />
)}
<TextInput
source="name"
validate={[required()]}
{...getNameHelperText()}
/>
<TextInput source="email" validate={[email()]} />
<PasswordInput source="password" validate={[required()]} />
{permissions === 'admin' && (
<BooleanInput source="isAdmin" initialValue={false} />
)}
<DateField variant="body1" source="lastLoginAt" showTime />
{/*<DateField source="lastAccessAt" showTime />*/}
<DateField variant="body1" source="updatedAt" showTime />
<DateField variant="body1" source="createdAt" showTime />
</SimpleForm>
</Edit>
)
}
export default UserEdit