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
ae733392
Commit
ae733392
authored
Aug 30, 2020
by
NGPixel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: password reset
parent
f98a07ec
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
174 additions
and
26 deletions
+174
-26
login.vue
client/components/login.vue
+101
-25
auth.js
server/controllers/auth.js
+22
-0
mail.js
server/core/mail.js
+1
-1
authentication.js
server/graph/resolvers/authentication.js
+13
-0
authentication.graphql
server/graph/schemas/authentication.graphql
+4
-0
users.js
server/models/users.js
+32
-0
account-reset-pwd.html
server/templates/account-reset-pwd.html
+0
-0
login.pug
server/views/login.pug
+1
-0
No files found.
client/components/login.vue
View file @
ae733392
...
@@ -253,6 +253,10 @@ export default {
...
@@ -253,6 +253,10 @@ export default {
hideLocal: {
hideLocal: {
type: Boolean,
type: Boolean,
default: false
default: false
},
changePwdContinuationToken: {
type: String,
default: null
}
}
},
},
data () {
data () {
...
@@ -309,6 +313,9 @@ export default {
...
@@ -309,6 +313,9 @@ export default {
},
},
selectedStrategyKey (newValue, oldValue) {
selectedStrategyKey (newValue, oldValue) {
this.selectedStrategy = _.find(this.strategies, ['key', newValue])
this.selectedStrategy = _.find(this.strategies, ['key', newValue])
if (this.screen === 'changePwd') {
return
}
this.screen = 'login'
this.screen = 'login'
if (!this.selectedStrategy.strategy.useForm) {
if (!this.selectedStrategy.strategy.useForm) {
this.isLoading = true
this.isLoading = true
...
@@ -322,6 +329,10 @@ export default {
...
@@ -322,6 +329,10 @@ export default {
},
},
mounted () {
mounted () {
this.isShown = true
this.isShown = true
if (this.changePwdContinuationToken) {
this.screen = 'changePwd'
this.continuationToken = this.changePwdContinuationToken
}
},
},
methods: {
methods: {
/**
/**
...
@@ -475,32 +486,51 @@ export default {
...
@@ -475,32 +486,51 @@ export default {
this.loaderColor = 'grey darken-4'
this.loaderColor = 'grey darken-4'
this.loaderTitle = this.$t('auth:changePwd.loading')
this.loaderTitle = this.$t('auth:changePwd.loading')
this.isLoading = true
this.isLoading = true
const resp = await this.$apollo.mutate({
try {
mutation: gql`
const resp = await this.$apollo.mutate({
{
mutation: gql`
authentication {
mutation (
activeStrategies {
$continuationToken: String!
key
$newPassword: String!
) {
authentication {
loginChangePassword (
continuationToken: $continuationToken
newPassword: $newPassword
) {
responseResult {
succeeded
errorCode
slug
message
}
jwt
continuationToken
redirect
}
}
}
}
}
`,
variables: {
continuationToken: this.continuationToken,
newPassword: this.newPassword
}
}
`,
})
variables: {
if (_.has(resp, 'data.authentication.loginChangePassword')) {
continuationToken: this.continuationToken,
let respObj = _.get(resp, 'data.authentication.loginChangePassword', {})
newPassword: this.newPassword
if (respObj.responseResult.succeeded === true) {
this.handleLoginResponse(respObj)
} else {
throw new Error(respObj.responseResult.message)
}
} else {
throw new Error(this.$t('auth:genericError'))
}
}
})
} catch (err) {
if (_.get(resp, 'data.authentication.loginChangePassword.responseResult.succeeded', false) === true) {
console.error(err)
this.loaderColor = 'green darken-1'
this.loaderTitle = this.$t('auth:loginSuccess')
Cookies.set('jwt', _.get(resp, 'data.authentication.loginChangePassword.jwt', ''), { expires: 365 })
_.delay(() => {
window.location.replace('/') // TEMPORARY - USE RETURNURL
}, 1000)
} else {
this.$store.commit('showNotification', {
this.$store.commit('showNotification', {
style: 'red',
style: 'red',
message:
_.get(resp, 'data.authentication.loginChangePassword.responseResult.message', false)
,
message:
err.message
,
icon: 'alert'
icon: 'alert'
})
})
this.isLoading = false
this.isLoading = false
...
@@ -519,11 +549,57 @@ export default {
...
@@ -519,11 +549,57 @@ export default {
* FORGOT PASSWORD SUBMIT
* FORGOT PASSWORD SUBMIT
*/
*/
async forgotPasswordSubmit () {
async forgotPasswordSubmit () {
this.$store.commit('showNotification', {
this.loaderColor = 'grey darken-4'
style: 'pink',
this.loaderTitle = this.$t('auth:forgotPasswordLoading')
message: 'Coming soon!',
this.isLoading = true
icon: 'ferry'
try {
})
const resp = await this.$apollo.mutate({
mutation: gql`
mutation (
$email: String!
) {
authentication {
forgotPassword (
email: $email
) {
responseResult {
succeeded
errorCode
slug
message
}
}
}
}
`,
variables: {
email: this.username
}
})
if (_.has(resp, 'data.authentication.forgotPassword.responseResult')) {
let respObj = _.get(resp, 'data.authentication.forgotPassword.responseResult', {})
if (respObj.succeeded === true) {
this.$store.commit('showNotification', {
style: 'success',
message: this.$t('auth:forgotPasswordSuccess'),
icon: 'email'
})
this.screen = 'login'
} else {
throw new Error(respObj.message)
}
} else {
throw new Error(this.$t('auth:genericError'))
}
} catch (err) {
console.error(err)
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'alert'
})
}
this.isLoading = false
},
},
handleLoginResponse (respObj) {
handleLoginResponse (respObj) {
this.continuationToken = respObj.continuationToken
this.continuationToken = respObj.continuationToken
...
...
server/controllers/auth.js
View file @
ae733392
...
@@ -149,6 +149,28 @@ router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
...
@@ -149,6 +149,28 @@ router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
})
})
/**
/**
* Reset Password
*/
router
.
get
(
'/login-reset/:token'
,
bruteforce
.
prevent
,
async
(
req
,
res
,
next
)
=>
{
try
{
const
usr
=
await
WIKI
.
models
.
userKeys
.
validateToken
({
kind
:
'resetPwd'
,
token
:
req
.
params
.
token
})
if
(
!
usr
)
{
throw
new
Error
(
'Invalid Token'
)
}
req
.
brute
.
reset
()
const
changePwdContinuationToken
=
await
WIKI
.
models
.
userKeys
.
generateToken
({
userId
:
usr
.
id
,
kind
:
'changePwd'
})
const
bgUrl
=
!
_
.
isEmpty
(
WIKI
.
config
.
auth
.
loginBgUrl
)
?
WIKI
.
config
.
auth
.
loginBgUrl
:
'/_assets/img/splash/1.jpg'
res
.
render
(
'login'
,
{
bgUrl
,
hideLocal
:
WIKI
.
config
.
auth
.
hideLocal
,
changePwdContinuationToken
})
}
catch
(
err
)
{
next
(
err
)
}
})
/**
* JWT Public Endpoints
* JWT Public Endpoints
*/
*/
router
.
get
(
'/.well-known/jwk.json'
,
function
(
req
,
res
,
next
)
{
router
.
get
(
'/.well-known/jwk.json'
,
function
(
req
,
res
,
next
)
{
...
...
server/core/mail.js
View file @
ae733392
...
@@ -56,7 +56,7 @@ module.exports = {
...
@@ -56,7 +56,7 @@ module.exports = {
subject
:
`
${
opts
.
subject
}
-
${
WIKI
.
config
.
title
}
`
,
subject
:
`
${
opts
.
subject
}
-
${
WIKI
.
config
.
title
}
`
,
text
:
opts
.
text
,
text
:
opts
.
text
,
html
:
_
.
get
(
this
.
templates
,
opts
.
template
)({
html
:
_
.
get
(
this
.
templates
,
opts
.
template
)({
logo
:
''
,
logo
:
WIKI
.
config
.
logoUrl
,
siteTitle
:
WIKI
.
config
.
title
,
siteTitle
:
WIKI
.
config
.
title
,
copyright
:
WIKI
.
config
.
company
.
length
>
0
?
WIKI
.
config
.
company
:
'Powered by Wiki.js'
,
copyright
:
WIKI
.
config
.
company
.
length
>
0
?
WIKI
.
config
.
company
:
'Powered by Wiki.js'
,
...
opts
.
data
...
opts
.
data
...
...
server/graph/resolvers/authentication.js
View file @
ae733392
...
@@ -138,6 +138,19 @@ module.exports = {
...
@@ -138,6 +138,19 @@ module.exports = {
}
}
},
},
/**
/**
* Perform Mandatory Password Change after Login
*/
async
forgotPassword
(
obj
,
args
,
context
)
{
try
{
await
WIKI
.
models
.
users
.
loginForgotPassword
(
args
,
context
)
return
{
responseResult
:
graphHelper
.
generateSuccess
(
'Password reset request processed.'
)
}
}
catch
(
err
)
{
return
graphHelper
.
generateError
(
err
)
}
},
/**
* Register a new account
* Register a new account
*/
*/
async
register
(
obj
,
args
,
context
)
{
async
register
(
obj
,
args
,
context
)
{
...
...
server/graph/schemas/authentication.graphql
View file @
ae733392
...
@@ -52,6 +52,10 @@ type AuthenticationMutation {
...
@@ -52,6 +52,10 @@ type AuthenticationMutation {
newPassword
:
String
!
newPassword
:
String
!
):
AuthenticationLoginResponse
@
rateLimit
(
limit
:
5
,
duration
:
60)
):
AuthenticationLoginResponse
@
rateLimit
(
limit
:
5
,
duration
:
60)
forgotPassword
(
email
:
String
!
):
DefaultResponse
@
rateLimit
(
limit
:
3
,
duration
:
60)
register
(
register
(
email
:
String
!
email
:
String
!
password
:
String
!
password
:
String
!
...
...
server/models/users.js
View file @
ae733392
...
@@ -479,6 +479,38 @@ module.exports = class User extends Model {
...
@@ -479,6 +479,38 @@ module.exports = class User extends Model {
}
}
/**
/**
* Send a password reset request
*/
static
async
loginForgotPassword
({
email
},
context
)
{
const
usr
=
await
WIKI
.
models
.
users
.
query
().
where
({
email
,
providerKey
:
'local'
}).
first
()
if
(
!
usr
)
{
WIKI
.
logger
.
debug
(
`Password reset attempt on nonexistant local account
${
email
}
: [DISCARDED]`
)
return
}
const
resetToken
=
await
WIKI
.
models
.
userKeys
.
generateToken
({
userId
:
usr
.
id
,
kind
:
'resetPwd'
})
await
WIKI
.
mail
.
send
({
template
:
'accountResetPwd'
,
to
:
email
,
subject
:
`Password Reset Request`
,
data
:
{
preheadertext
:
`A password reset was requested for
${
WIKI
.
config
.
title
}
`
,
title
:
`A password reset was requested for
${
WIKI
.
config
.
title
}
`
,
content
:
`Click the button below to reset your password. If you didn't request this password reset, simply discard this email.`
,
buttonLink
:
`
${
WIKI
.
config
.
host
}
/login-reset/
${
resetToken
}
`
,
buttonText
:
'Reset Password'
},
text
:
`A password reset was requested for wiki
${
WIKI
.
config
.
title
}
. Open the following link to proceed:
${
WIKI
.
config
.
host
}
/login-reset/
${
resetToken
}
`
})
}
/**
* Create a new user
* Create a new user
*
*
* @param {Object} param0 User Fields
* @param {Object} param0 User Fields
...
...
server/templates/account-reset-pwd.html
0 → 100644
View file @
ae733392
This diff is collapsed.
Click to expand it.
server/views/login.pug
View file @
ae733392
...
@@ -5,4 +5,5 @@ block body
...
@@ -5,4 +5,5 @@ block body
login(
login(
bg-url=bgUrl
bg-url=bgUrl
hide-local=hideLocal
hide-local=hideLocal
change-pwd-continuation-token=changePwdContinuationToken
)
)
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