Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wiki-js
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
wiki-js
Commits
7f9c3511
Unverified
Commit
7f9c3511
authored
Oct 02, 2023
by
NGPixel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: change own password dialog
parent
c5a441c9
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
348 additions
and
16 deletions
+348
-16
authentication.mjs
server/graph/resolvers/authentication.mjs
+7
-0
user.mjs
server/graph/resolvers/user.mjs
+6
-5
authentication.graphql
server/graph/schemas/authentication.graphql
+1
-2
user.graphql
server/graph/schemas/user.graphql
+7
-0
en.json
server/locales/en.json
+3
-1
users.mjs
server/models/users.mjs
+50
-2
ultraviolet-good-pincode.svg
ux/public/_assets/icons/ultraviolet-good-pincode.svg
+2
-0
ultraviolet-lock.svg
ux/public/_assets/icons/ultraviolet-lock.svg
+2
-0
AuthLoginPanel.vue
ux/src/components/AuthLoginPanel.vue
+1
-1
ChangePwdDialog.vue
ux/src/components/ChangePwdDialog.vue
+248
-0
UserEditOverlay.vue
ux/src/components/UserEditOverlay.vue
+7
-2
ProfileAuth.vue
ux/src/pages/ProfileAuth.vue
+14
-3
No files found.
server/graph/resolvers/authentication.mjs
View file @
7f9c3511
...
@@ -127,11 +127,18 @@ export default {
...
@@ -127,11 +127,18 @@ export default {
*/
*/
async
changePassword
(
obj
,
args
,
context
)
{
async
changePassword
(
obj
,
args
,
context
)
{
try
{
try
{
if
(
args
.
continuationToken
)
{
const
authResult
=
await
WIKI
.
db
.
users
.
loginChangePassword
(
args
,
context
)
const
authResult
=
await
WIKI
.
db
.
users
.
loginChangePassword
(
args
,
context
)
return
{
return
{
...
authResult
,
...
authResult
,
operation
:
generateSuccess
(
'Password set successfully'
)
}
}
else
{
await
WIKI
.
db
.
users
.
changePassword
(
args
,
context
)
return
{
operation
:
generateSuccess
(
'Password changed successfully'
)
operation
:
generateSuccess
(
'Password changed successfully'
)
}
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
WIKI
.
logger
.
debug
(
err
)
WIKI
.
logger
.
debug
(
err
)
return
generateError
(
err
)
return
generateError
(
err
)
...
...
server/graph/resolvers/user.mjs
View file @
7f9c3511
...
@@ -41,7 +41,7 @@ export default {
...
@@ -41,7 +41,7 @@ export default {
const
usr
=
await
WIKI
.
db
.
users
.
query
().
findById
(
args
.
id
)
const
usr
=
await
WIKI
.
db
.
users
.
query
().
findById
(
args
.
id
)
if
(
!
usr
)
{
if
(
!
usr
)
{
throw
new
Error
(
'
Invalid User
'
)
throw
new
Error
(
'
ERR_INVALID_USER
'
)
}
}
// const str = _.get(WIKI.auth.strategies, usr.providerKey)
// const str = _.get(WIKI.auth.strategies, usr.providerKey)
...
@@ -51,10 +51,11 @@ export default {
...
@@ -51,10 +51,11 @@ export default {
usr
.
auth
=
_
.
mapValues
(
usr
.
auth
,
(
auth
,
providerKey
)
=>
{
usr
.
auth
=
_
.
mapValues
(
usr
.
auth
,
(
auth
,
providerKey
)
=>
{
if
(
auth
.
password
)
{
if
(
auth
.
password
)
{
auth
.
password
=
'***'
auth
.
password
=
'redacted'
}
if
(
auth
.
tfaSecret
)
{
auth
.
tfaSecret
=
'redacted'
}
}
auth
.
module
=
providerKey
===
'00910749-8ab6-498a-9be0-f4ca28ea5e52'
?
'google'
:
'local'
auth
.
_moduleName
=
providerKey
===
'00910749-8ab6-498a-9be0-f4ca28ea5e52'
?
'Google'
:
'Local'
return
auth
return
auth
})
})
...
@@ -211,7 +212,7 @@ export default {
...
@@ -211,7 +212,7 @@ export default {
},
},
async
changeUserPassword
(
obj
,
args
,
context
)
{
async
changeUserPassword
(
obj
,
args
,
context
)
{
try
{
try
{
if
(
args
.
newPassword
?.
length
<
6
)
{
if
(
args
.
newPassword
?.
length
<
8
)
{
throw
new
Error
(
'ERR_PASSWORD_TOO_SHORT'
)
throw
new
Error
(
'ERR_PASSWORD_TOO_SHORT'
)
}
}
...
...
server/graph/schemas/authentication.graphql
View file @
7f9c3511
...
@@ -42,12 +42,11 @@ extend type Mutation {
...
@@ -42,12 +42,11 @@ extend type Mutation {
):
AuthenticationAuthResponse
@
rateLimit
(
limit
:
5
,
duration
:
60)
):
AuthenticationAuthResponse
@
rateLimit
(
limit
:
5
,
duration
:
60)
changePassword
(
changePassword
(
userId
:
UUID
continuationToken
:
String
continuationToken
:
String
currentPassword
:
String
currentPassword
:
String
newPassword
:
String
!
newPassword
:
String
!
strategyId
:
UUID
!
strategyId
:
UUID
!
siteId
:
UUID
siteId
:
UUID
!
):
AuthenticationAuthResponse
@
rateLimit
(
limit
:
5
,
duration
:
60)
):
AuthenticationAuthResponse
@
rateLimit
(
limit
:
5
,
duration
:
60)
forgotPassword
(
forgotPassword
(
...
...
server/graph/schemas/user.graphql
View file @
7f9c3511
...
@@ -189,8 +189,15 @@ input UserUpdateInput {
...
@@ -189,8 +189,15 @@ input UserUpdateInput {
email
:
String
email
:
String
name
:
String
name
:
String
groups
:
[
UUID
!]
groups
:
[
UUID
!]
auth
:
UserAuthUpdateInput
isActive
:
Boolean
isActive
:
Boolean
isVerified
:
Boolean
isVerified
:
Boolean
meta
:
JSON
meta
:
JSON
prefs
:
JSON
prefs
:
JSON
}
}
input
UserAuthUpdateInput
{
tfaRequired
:
Boolean
mustChangePwd
:
Boolean
restrictLogin
:
Boolean
}
server/locales/en.json
View file @
7f9c3511
...
@@ -1152,6 +1152,7 @@
...
@@ -1152,6 +1152,7 @@
"auth.errors.tooManyAttempts"
:
"Too many attempts!"
,
"auth.errors.tooManyAttempts"
:
"Too many attempts!"
,
"auth.errors.tooManyAttemptsMsg"
:
"You've made too many failed attempts in a short period of time, please try again {time}."
,
"auth.errors.tooManyAttemptsMsg"
:
"You've made too many failed attempts in a short period of time, please try again {time}."
,
"auth.errors.userNotFound"
:
"User not found"
,
"auth.errors.userNotFound"
:
"User not found"
,
"auth.errors.fields"
:
"One or more fields are invalid."
,
"auth.fields.email"
:
"Email Address"
,
"auth.fields.email"
:
"Email Address"
,
"auth.fields.emailUser"
:
"Email / Username"
,
"auth.fields.emailUser"
:
"Email / Username"
,
"auth.fields.name"
:
"Name"
,
"auth.fields.name"
:
"Name"
,
...
@@ -1197,9 +1198,9 @@
...
@@ -1197,9 +1198,9 @@
"auth.tfaFormTitle"
:
"Enter the security code generated from your trusted device:"
,
"auth.tfaFormTitle"
:
"Enter the security code generated from your trusted device:"
,
"auth.tfaSetupInstrFirst"
:
"Scan the QR code below from your mobile 2FA application:"
,
"auth.tfaSetupInstrFirst"
:
"Scan the QR code below from your mobile 2FA application:"
,
"auth.tfaSetupInstrSecond"
:
"Enter the security code generated from your trusted device:"
,
"auth.tfaSetupInstrSecond"
:
"Enter the security code generated from your trusted device:"
,
"auth.tfaSetupSuccess"
:
"2FA enabled successfully on your account."
,
"auth.tfaSetupTitle"
:
"Your administrator has required Two-Factor Authentication (2FA) to be enabled on your account."
,
"auth.tfaSetupTitle"
:
"Your administrator has required Two-Factor Authentication (2FA) to be enabled on your account."
,
"auth.tfaSetupVerifying"
:
"Verifying..."
,
"auth.tfaSetupVerifying"
:
"Verifying..."
,
"auth.tfaSetupSuccess"
:
"2FA enabled successfully on your account."
,
"common.actions.activate"
:
"Activate"
,
"common.actions.activate"
:
"Activate"
,
"common.actions.add"
:
"Add"
,
"common.actions.add"
:
"Add"
,
"common.actions.apply"
:
"Apply"
,
"common.actions.apply"
:
"Apply"
,
...
@@ -1746,6 +1747,7 @@
...
@@ -1746,6 +1747,7 @@
"profile.appearanceLight"
:
"Light"
,
"profile.appearanceLight"
:
"Light"
,
"profile.auth"
:
"Authentication"
,
"profile.auth"
:
"Authentication"
,
"profile.authChangePassword"
:
"Change Password"
,
"profile.authChangePassword"
:
"Change Password"
,
"profile.authDisableTfa"
:
"Turn Off 2FA"
,
"profile.authInfo"
:
"Your account is associated with the following authentication methods:"
,
"profile.authInfo"
:
"Your account is associated with the following authentication methods:"
,
"profile.authLoadingFailed"
:
"Failed to load authentication methods."
,
"profile.authLoadingFailed"
:
"Failed to load authentication methods."
,
"profile.authModifyTfa"
:
"Modify 2FA"
,
"profile.authModifyTfa"
:
"Modify 2FA"
,
...
...
server/models/users.mjs
View file @
7f9c3511
...
@@ -498,6 +498,42 @@ export class User extends Model {
...
@@ -498,6 +498,42 @@ export class User extends Model {
}
}
/**
/**
* Change Password from Profile
*/
static
async
changePassword
({
strategyId
,
siteId
,
currentPassword
,
newPassword
},
context
)
{
const
userId
=
context
.
req
.
user
?.
id
if
(
!
userId
)
{
throw
new
Error
(
'ERR_USER_NOT_AUTHENTICATED'
)
}
const
user
=
await
WIKI
.
db
.
users
.
query
().
findById
(
userId
)
if
(
!
user
)
{
throw
new
Error
(
'ERR_USER_NOT_FOUND'
)
}
if
(
!
newPassword
||
newPassword
.
length
<
8
)
{
throw
new
Error
(
'ERR_PASSWORD_TOO_SHORT'
)
}
if
(
!
user
.
auth
[
strategyId
]?.
password
)
{
throw
new
Error
(
'ERR_UNEXPECTED_STRATEGY_ID'
)
}
if
(
await
bcrypt
.
compare
(
currentPassword
,
user
.
auth
[
strategyId
].
password
)
!==
true
)
{
throw
new
Error
(
'ERR_INCORRECT_CURRENT_PASSWORD'
)
}
user
.
auth
[
strategyId
].
password
=
await
bcrypt
.
hash
(
newPassword
,
12
)
user
.
auth
[
strategyId
].
mustChangePwd
=
false
await
user
.
$query
().
patch
({
auth
:
user
.
auth
})
return
true
}
/**
* Send a password reset request
* Send a password reset request
*/
*/
static
async
loginForgotPassword
({
email
},
context
)
{
static
async
loginForgotPassword
({
email
},
context
)
{
...
@@ -686,14 +722,14 @@ export class User extends Model {
...
@@ -686,14 +722,14 @@ export class User extends Model {
*
*
* @param {Object} param0 User ID and fields to update
* @param {Object} param0 User ID and fields to update
*/
*/
static
async
updateUser
(
id
,
{
email
,
name
,
groups
,
isVerified
,
isActive
,
meta
,
prefs
})
{
static
async
updateUser
(
id
,
{
email
,
name
,
groups
,
auth
,
isVerified
,
isActive
,
meta
,
prefs
})
{
const
usr
=
await
WIKI
.
db
.
users
.
query
().
findById
(
id
)
const
usr
=
await
WIKI
.
db
.
users
.
query
().
findById
(
id
)
if
(
usr
)
{
if
(
usr
)
{
let
usrData
=
{}
let
usrData
=
{}
if
(
!
isEmpty
(
email
)
&&
email
!==
usr
.
email
)
{
if
(
!
isEmpty
(
email
)
&&
email
!==
usr
.
email
)
{
const
dupUsr
=
await
WIKI
.
db
.
users
.
query
().
select
(
'id'
).
where
({
email
}).
first
()
const
dupUsr
=
await
WIKI
.
db
.
users
.
query
().
select
(
'id'
).
where
({
email
}).
first
()
if
(
dupUsr
)
{
if
(
dupUsr
)
{
throw
new
WIKI
.
Error
.
AuthAccountAlreadyExists
(
)
throw
new
Error
(
'ERR_DUPLICATE_ACCOUNT_EMAIL'
)
}
}
usrData
.
email
=
email
.
toLowerCase
()
usrData
.
email
=
email
.
toLowerCase
()
}
}
...
@@ -714,6 +750,18 @@ export class User extends Model {
...
@@ -714,6 +750,18 @@ export class User extends Model {
await
usr
.
$relatedQuery
(
'groups'
).
unrelate
().
where
(
'groupId'
,
grp
)
await
usr
.
$relatedQuery
(
'groups'
).
unrelate
().
where
(
'groupId'
,
grp
)
}
}
}
}
if
(
!
isNil
(
auth
?.
tfaRequired
))
{
usr
.
auth
[
WIKI
.
data
.
systemIds
.
localAuthId
].
tfaRequired
=
auth
.
tfaRequired
usrData
.
auth
=
usr
.
auth
}
if
(
!
isNil
(
auth
?.
mustChangePwd
))
{
usr
.
auth
[
WIKI
.
data
.
systemIds
.
localAuthId
].
mustChangePwd
=
auth
.
mustChangePwd
usrData
.
auth
=
usr
.
auth
}
if
(
!
isNil
(
auth
?.
restrictLogin
))
{
usr
.
auth
[
WIKI
.
data
.
systemIds
.
localAuthId
].
restrictLogin
=
auth
.
restrictLogin
usrData
.
auth
=
usr
.
auth
}
if
(
!
isNil
(
isVerified
))
{
if
(
!
isNil
(
isVerified
))
{
usrData
.
isVerified
=
isVerified
usrData
.
isVerified
=
isVerified
}
}
...
...
ux/public/_assets/icons/ultraviolet-good-pincode.svg
0 → 100644
View file @
7f9c3511
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 40 40"
width=
"80px"
height=
"80px"
><path
fill=
"#98ccfd"
d=
"M0.5 2.5H39.5V19.5H0.5z"
/><path
fill=
"#4788c7"
d=
"M39,3v16H1V3H39 M40,2H0v18h40V2L40,2z"
/><path
fill=
"#fff"
d=
"M18 11c0 1.133-.867 2-2 2s-2-.867-2-2 .867-2 2-2S18 9.867 18 11zM8 9c-1.133 0-2 .867-2 2s.867 2 2 2 2-.867 2-2S9.133 9 8 9zM32 9c-1.133 0-2 .867-2 2s.867 2 2 2c1.133 0 2-.867 2-2S33.133 9 32 9zM24 9c-1.133 0-2 .867-2 2s.867 2 2 2 2-.867 2-2S25.133 9 24 9z"
/><g><path
fill=
"#dff0fe"
d=
"M10.707 31L13.015 28.692 17.087 32.763 27.072 22.779 29.293 25 17 37.293z"
/><path
fill=
"#4788c7"
d=
"M27.072,23.487L28.586,25L17,36.586L11.414,31l1.601-1.601l3.365,3.364l0.707,0.707l0.707-0.707 L27.072,23.487 M27.073,22.073l-9.986,9.983l-4.072-4.071L10,31l7,7.001L30,25L27.073,22.073L27.073,22.073z"
/></g></svg>
\ No newline at end of file
ux/public/_assets/icons/ultraviolet-lock.svg
0 → 100644
View file @
7f9c3511
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 40 40"
width=
"80px"
height=
"80px"
><path
fill=
"none"
stroke=
"#4788c7"
stroke-miterlimit=
"10"
stroke-width=
"2"
d=
"M30,17.714c0,0,0-5.306,0-5.714 c0-5.523-4.477-10-10-10S10,6.477,10,12c0,0.408,0,5.714,0,5.714"
/><path
fill=
"#dff0fe"
d=
"M2.5,37.5V22c0-3.584,2.916-6.5,6.5-6.5h22c3.584,0,6.5,2.916,6.5,6.5v15.5H2.5z"
/><path
fill=
"#4788c7"
d=
"M31,16c3.308,0,6,2.692,6,6v15H3V22c0-3.308,2.692-6,6-6H31 M31,15H9c-3.866,0-7,3.134-7,7v16h36V22 C38,18.134,34.866,15,31,15L31,15z"
/><g><path
fill=
"#b6dcfe"
d=
"M17.59,32.5l0.891-5.343l-0.289-0.176C17.133,26.336,16.5,25.222,16.5,24c0-1.93,1.57-3.5,3.5-3.5 s3.5,1.57,3.5,3.5c0,1.222-0.633,2.336-1.691,2.981l-0.289,0.176L22.41,32.5H17.59z"
/><path
fill=
"#4788c7"
d=
"M20,21c1.654,0,3,1.346,3,3c0,1.046-0.543,2.001-1.452,2.554l-0.578,0.352l0.111,0.667L21.82,32 H18.18l0.738-4.427l0.111-0.667l-0.578-0.352C17.543,26.001,17,25.046,17,24C17,22.346,18.346,21,20,21 M20,20 c-2.209,0-4,1.791-4,4c0,1.449,0.778,2.707,1.932,3.408L17,33h6l-0.932-5.592C23.222,26.707,24,25.449,24,24 C24,21.791,22.209,20,20,20L20,20z"
/></g></svg>
\ No newline at end of file
ux/src/components/AuthLoginPanel.vue
View file @
7f9c3511
...
@@ -703,7 +703,7 @@ async function changePwd () {
...
@@ -703,7 +703,7 @@ async function changePwd () {
$continuationToken: String
$continuationToken: String
$newPassword: String!
$newPassword: String!
$strategyId: UUID!
$strategyId: UUID!
$siteId: UUID
$siteId: UUID
!
) {
) {
changePassword (
changePassword (
continuationToken: $continuationToken
continuationToken: $continuationToken
...
...
ux/src/components/ChangePwdDialog.vue
0 → 100644
View file @
7f9c3511
<
template
lang=
"pug"
>
q-dialog(ref='dialogRef', @hide='onDialogHide')
q-card(style='min-width: 650px;')
q-card-section.card-header
q-icon(name='img:/_assets/icons/fluent-password-reset.svg', left, size='sm')
span
{{
t
(
`admin.users.changePassword`
)
}}
q-form.q-py-sm(ref='changeUserPwdForm', @submit='save')
q-item
blueprint-icon(icon='lock')
q-item-section
q-input(
outlined
v-model='state.currentPassword'
dense
:rules='currentPasswordValidation'
hide-bottom-space
:label='t(`auth.changePwd.currentPassword`)'
:aria-label='t(`auth.changePwd.currentPassword`)'
lazy-rules='ondemand'
autofocus
)
q-item
blueprint-icon(icon='password')
q-item-section
q-input(
outlined
v-model='state.newPassword'
dense
:rules='newPasswordValidation'
hide-bottom-space
:label='t(`auth.changePwd.newPassword`)'
:aria-label='t(`auth.changePwd.newPassword`)'
lazy-rules='ondemand'
autofocus
)
template(#append)
.flex.items-center
q-badge(
:color='passwordStrength.color'
:label='passwordStrength.label'
)
q-separator.q-mx-sm(vertical)
q-btn(
flat
dense
padding='none xs'
color='brown'
@click='randomizePassword'
)
q-icon(name='las la-dice-d6')
.q-pl-xs.text-caption: strong Generate
q-item
blueprint-icon(icon='good-pincode')
q-item-section
q-input(
outlined
v-model='state.verifyPassword'
dense
:rules='verifyPasswordValidation'
hide-bottom-space
:label='t(`auth.changePwd.newPasswordVerify`)'
:aria-label='t(`auth.changePwd.newPasswordVerify`)'
lazy-rules='ondemand'
autofocus
)
q-card-actions.card-actions
q-space
q-btn.acrylic-btn(
flat
:label='t(`common.actions.cancel`)'
color='grey'
padding='xs md'
@click='onDialogCancel'
)
q-btn(
unelevated
:label='t(`common.actions.update`)'
color='primary'
padding='xs md'
@click='save'
:loading='state.isLoading'
)
</
template
>
<
script
setup
>
import
gql
from
'graphql-tag'
import
zxcvbn
from
'zxcvbn'
import
{
sampleSize
}
from
'lodash-es'
import
{
useI18n
}
from
'vue-i18n'
import
{
useDialogPluginComponent
,
useQuasar
}
from
'quasar'
import
{
computed
,
reactive
,
ref
}
from
'vue'
import
{
useSiteStore
}
from
'src/stores/site'
// PROPS
const
props
=
defineProps
({
strategyId
:
{
type
:
String
,
required
:
true
}
})
// EMITS
defineEmits
([
...
useDialogPluginComponent
.
emits
])
// QUASAR
const
{
dialogRef
,
onDialogHide
,
onDialogOK
,
onDialogCancel
}
=
useDialogPluginComponent
()
const
$q
=
useQuasar
()
// STORES
const
siteStore
=
useSiteStore
()
// I18N
const
{
t
}
=
useI18n
()
// DATA
const
state
=
reactive
({
currentPassword
:
''
,
newPassword
:
''
,
verifyPassword
:
''
,
isLoading
:
false
})
// REFS
const
changeUserPwdForm
=
ref
(
null
)
// COMPUTED
const
passwordStrength
=
computed
(()
=>
{
if
(
state
.
newPassword
.
length
<
8
)
{
return
{
color
:
'negative'
,
label
:
t
(
'admin.users.pwdStrengthWeak'
)
}
}
else
{
switch
(
zxcvbn
(
state
.
newPassword
).
score
)
{
case
1
:
return
{
color
:
'deep-orange-7'
,
label
:
t
(
'admin.users.pwdStrengthPoor'
)
}
case
2
:
return
{
color
:
'purple-7'
,
label
:
t
(
'admin.users.pwdStrengthMedium'
)
}
case
3
:
return
{
color
:
'blue-7'
,
label
:
t
(
'admin.users.pwdStrengthGood'
)
}
case
4
:
return
{
color
:
'green-7'
,
label
:
t
(
'admin.users.pwdStrengthStrong'
)
}
default
:
return
{
color
:
'negative'
,
label
:
t
(
'admin.users.pwdStrengthWeak'
)
}
}
}
})
// VALIDATION RULES
const
currentPasswordValidation
=
[
val
=>
val
.
length
>
0
||
t
(
'auth.errors.missingPassword'
)
]
const
newPasswordValidation
=
[
val
=>
val
.
length
>
0
||
t
(
'auth.errors.missingPassword'
),
val
=>
val
.
length
>=
8
||
t
(
'auth.errors.passwordTooShort'
)
]
const
verifyPasswordValidation
=
[
val
=>
val
.
length
>
0
||
t
(
'auth.errors.missingVerifyPassword'
),
val
=>
val
===
state
.
newPassword
||
t
(
'auth.errors.passwordsNotMatch'
)
]
// METHODS
function
randomizePassword
()
{
const
pwdChars
=
'abcdefghkmnpqrstuvwxyzABCDEFHJKLMNPQRSTUVWXYZ23456789_*=?#!()+'
state
.
newPassword
=
sampleSize
(
pwdChars
,
16
).
join
(
''
)
}
async
function
save
()
{
state
.
isLoading
=
true
try
{
const
isFormValid
=
await
changeUserPwdForm
.
value
.
validate
(
true
)
if
(
!
isFormValid
)
{
throw
new
Error
(
t
(
'auth.errors.fields'
))
}
const
resp
=
await
APOLLO_CLIENT
.
mutate
({
mutation
:
gql
`
mutation changePwd (
$currentPassword: String
$newPassword: String!
$strategyId: UUID!
$siteId: UUID!
) {
changePassword (
currentPassword: $currentPassword
newPassword: $newPassword
strategyId: $strategyId
siteId: $siteId
) {
operation {
succeeded
message
}
}
}
`
,
variables
:
{
currentPassword
:
state
.
currentPassword
,
newPassword
:
state
.
newPassword
,
strategyId
:
props
.
strategyId
,
siteId
:
siteStore
.
id
}
})
if
(
resp
?.
data
?.
changePassword
?.
operation
?.
succeeded
)
{
$q
.
notify
({
type
:
'positive'
,
message
:
t
(
'auth.changePwd.success'
)
})
onDialogOK
()
}
else
{
throw
new
Error
(
resp
?.
data
?.
changePassword
?.
operation
?.
message
||
'An unexpected error occured.'
)
}
}
catch
(
err
)
{
$q
.
notify
({
type
:
'negative'
,
message
:
err
.
message
})
}
state
.
isLoading
=
false
}
</
script
>
ux/src/components/UserEditOverlay.vue
View file @
7f9c3511
...
@@ -744,7 +744,12 @@ async function save (patch, { silent, keepOpen } = { silent: false, keepOpen: fa
...
@@ -744,7 +744,12 @@ async function save (patch, { silent, keepOpen } = { silent: false, keepOpen: fa
isActive
:
state
.
user
.
isActive
,
isActive
:
state
.
user
.
isActive
,
meta
:
state
.
user
.
meta
,
meta
:
state
.
user
.
meta
,
prefs
:
state
.
user
.
prefs
,
prefs
:
state
.
user
.
prefs
,
groups
:
state
.
user
.
groups
.
map
(
gr
=>
gr
.
id
)
groups
:
state
.
user
.
groups
.
map
(
gr
=>
gr
.
id
),
auth
:
{
tfaRequired
:
localAuth
.
value
.
isTfaRequired
,
mustChangePwd
:
localAuth
.
value
.
mustChangePwd
,
restrictLogin
:
localAuth
.
value
.
restrictLogin
}
}
}
}
}
try
{
try
{
...
@@ -816,7 +821,7 @@ function invalidateTFA () {
...
@@ -816,7 +821,7 @@ function invalidateTFA () {
label
:
t
(
'common.actions.confirm'
)
label
:
t
(
'common.actions.confirm'
)
}
}
}).
onOk
(()
=>
{
}).
onOk
(()
=>
{
localAuth
.
value
.
tfaSecret
=
''
// TODO: invalidate user 2FA
$q
.
notify
({
$q
.
notify
({
type
:
'positive'
,
type
:
'positive'
,
message
:
t
(
'admin.users.tfaInvalidateSuccess'
)
message
:
t
(
'admin.users.tfaInvalidateSuccess'
)
...
...
ux/src/pages/ProfileAuth.vue
View file @
7f9c3511
...
@@ -25,8 +25,8 @@ q-page.q-py-md(:style-fn='pageStyle')
...
@@ -25,8 +25,8 @@ q-page.q-py-md(:style-fn='pageStyle')
q-btn(
q-btn(
icon='las la-fingerprint'
icon='las la-fingerprint'
unelevated
unelevated
:label='t(`profile.auth
Modify
Tfa`)'
:label='t(`profile.auth
Disable
Tfa`)'
color='
primary
'
color='
negative
'
@click=''
@click=''
)
)
q-item-section(v-else, side)
q-item-section(v-else, side)
...
@@ -43,7 +43,7 @@ q-page.q-py-md(:style-fn='pageStyle')
...
@@ -43,7 +43,7 @@ q-page.q-py-md(:style-fn='pageStyle')
unelevated
unelevated
:label='t(`profile.authChangePassword`)'
:label='t(`profile.authChangePassword`)'
color='primary'
color='primary'
@click=''
@click='
changePassword(auth.authId)
'
)
)
q-inner-loading(:showing='state.loading > 0')
q-inner-loading(:showing='state.loading > 0')
...
@@ -57,6 +57,8 @@ import { onMounted, reactive } from 'vue'
...
@@ -57,6 +57,8 @@ import { onMounted, reactive } from 'vue'
import
{
useUserStore
}
from
'src/stores/user'
import
{
useUserStore
}
from
'src/stores/user'
import
ChangePwdDialog
from
'src/components/ChangePwdDialog.vue'
// QUASAR
// QUASAR
const
$q
=
useQuasar
()
const
$q
=
useQuasar
()
...
@@ -128,6 +130,15 @@ async function fetchAuthMethods () {
...
@@ -128,6 +130,15 @@ async function fetchAuthMethods () {
state
.
loading
--
state
.
loading
--
}
}
function
changePassword
(
strategyId
)
{
$q
.
dialog
({
component
:
ChangePwdDialog
,
componentProps
:
{
strategyId
}
})
}
// MOUNTED
// MOUNTED
onMounted
(()
=>
{
onMounted
(()
=>
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment