teacup/password.xqm

217 lines
8.5 KiB
Plaintext

(:~
: User's Password RESTXQ functions.
:
: @author Rave Technologies, https://www.rave-tech.com/, 2017
:)
module namespace blpass = 'http://www.rave-tech.com/bloomsbury/password';
import module namespace config = 'http://www.rave-tech.com/bloomsbury/config' at './module/config.xqm';
import module namespace audit = 'http://www.rave-tech.com/bloomsbury/audit' at './module/manage-audit.xqm';
import module namespace Request = 'http://exquery.org/ns/request';
(:~
: Create a token to set user password.
: @param $body as document node <email>user1500547727558</email>
: @return element(result)
:)
declare
%updating
%rest:path("/reset-password")
%rest:POST("{$body}")
%rest:consumes("application/xml", "text/xml")
function blpass:set(
$body as document-node()
)
{
let $email := $body/email/text()
let $userXml := db:open($config:CoreDatabase,$config:UserDir)/user[email=$email]
[fn:not(fn:matches(fn:base-uri(.),'(/version/|/audit/)'))]
let $userID := $userXml/id/text()
let $userName := $userXml/name/text()
return
if($userXml)
then
(:if($userXml/user[password[xs:dateTime(@expire-on/data()) > fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))]])
then
(
config:non-update-message("[Set Password][Password token already exist for the user : " || $userID || "]"),
update:output(<result><status>Failure</status><message>Password token already exist for this user</message></result>)
)
else:)
if($userXml[active='No'])
then
(
config:update-message("[Set Password][This user is not active : " || $userID || "]"),
update:output(<result><status>Failure</status><message>This user is not active. Please contact administrator</message></result>)
)
else
if($userXml[locked='Yes'])
then
(
config:update-message("[Set Password][This user is locked : " || $userID || "]"),
update:output(<result><status>Failure</status><message>This user is locked. Please contact administrator</message></result>)
)
else
try
{
let $passwordToken := fn:concat($config:PasswordTokenPrefix,fn:string(convert:dateTime-to-integer(fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms())))))
let $tokenExpireTime := fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms())) + xs:dayTimeDuration('P2D')
return
if($userXml/password)
then
(
replace node $userXml/password with <password tokenid="{$passwordToken}" expire-on="{xs:dateTime($tokenExpireTime)}"/>,
config:update-message("[Set Password][Password token as been set for the user : " || $userID || "]"),
update:output(<result><status>Success</status><name>{$userName}</name><ptoken>{$passwordToken}</ptoken></result>)
)
else
(
insert node <password tokenid="{$passwordToken}" expire-on="{xs:dateTime($tokenExpireTime)}"/> as last into $userXml,
config:update-message("[Set Password][Password token as been set for the user : " || $userID || "]"),
update:output(<result><status>Success</status><name>{$userName}</name><ptoken>{$passwordToken}</ptoken></result>)
)
}
catch *
{
admin:write-log("[Set Password][Error: " || $err:description || "]"),
admin:write-log("[Set Password][Error: " || $err:additional || "]"),
update:output(<result><status>Error</status><message>{$err:description}</message></result>)
}
else
(
config:update-message("[Set Password][User is unavailable]"),
update:output(<result><status>Failure</status><message>User is unavailable</message></result>)
)
};
(:~
: Check Password token validity.
: @param $token Password token to check validity
: @return element(result)
:)
declare
%rest:path("/reset-password/{$token=.+}")
%rest:GET
function blpass:check(
$token as xs:string
)
{
try
{
if(db:open($config:CoreDatabase,$config:UserDir)/user/password[@tokenid=$token]
[xs:dateTime(@expire-on/data()) > fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))]
[fn:not(fn:matches(fn:base-uri(.),'(/version/|/audit/)'))]
)
then
(
<result><status>Success</status><message>The link is valid</message></result>,
config:non-update-message("[Check Password][Password token is valid : " || $token || "]")
)
else
if(db:open($config:CoreDatabase,$config:UserDir)/user/password[@tokenid=$token]
[xs:dateTime(@expire-on/data()) le fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))]
[fn:not(fn:matches(fn:base-uri(.),'(/version/|/audit/)'))]
)
then
(
<result><status>Failure</status><message>The password token has expired</message></result>,
config:non-update-message("[Check Password][The password token has expired : " || $token || "]")
)
else <result><status>Failure</status><message>Invalid password token</message></result>
}
catch *
{
admin:write-log("[Check Password][Error: " || $err:description || "]"),
admin:write-log("[Check Password][Error: " || $err:additional || "]"),
<result><status>Error</status><message>{$err:description}</message></result>
}
};
(:~
: Delete Password token.
: @param $token Password token to check validity
: @return element(result)
:)
declare
%updating
%rest:path("/reset-password/{$token=.+}")
%rest:DELETE
%rest:consumes("application/xml", "text/xml")
function blpass:delete(
$token as xs:string
)
{
try
{
if(db:open($config:CoreDatabase,$config:UserDir)/user/password[@tokenid=$token]
[fn:not(fn:matches(fn:base-uri(.),'(/version/|/audit/)'))])
then
(
delete node db:open($config:CoreDatabase,$config:UserDir)/user/password[@tokenid=$token][not(matches(base-uri(.),'(/version/|/audit/)'))],
config:update-message("[Delete Password][Password token deleted : " || $token || "]"),
update:output(<result><status>Success</status><message>Password token deleted</message></result>)
)
else
(
config:update-message("[Delete Password][Invalid password token : " || $token || "]"),
update:output(<result><status>Failure</status><message>Invalid password token for the user</message></result>)
)
}
catch *
{
admin:write-log("[Delete Password][Error: " || $err:description || "]"),
admin:write-log("[Delete Password][Error: " || $err:additional || "]"),
update:output(<result><status>Error</status><message>{$err:description}</message></result>)
}
};
(:~
: Reset user password.
: @param $body as document node <password>#@%@#@#</password>
: @return element(result)
:)
declare
%updating
%rest:path("/reset-password/{$token=.+}")
%rest:PUT("{$body}")
%rest:consumes("application/xml", "text/xml")
function blpass:reset(
$body as document-node(),
$token as xs:string
)
{
try
{
let $userXml := db:open($config:CoreDatabase,$config:UserDir)/user[password/@tokenid=$token]
[fn:not(fn:matches(fn:base-uri(.),'(/version/|/audit/)'))]
let $email := $userXml/email/text()
let $userID := $userXml/id/text()
let $username := $userXml/name/text()
return
if($userXml/password[xs:dateTime(@expire-on/data()) > fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))])
then
(
user:password($email, $body/password/text()),
delete node $userXml/password,
replace node $userXml/password-updated with <password-updated>{fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))}</password-updated>,
replace node $userXml/updated with <updated>{fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))}</updated>,
audit:user($userID, 'Reset Password'),
config:update-message("[Set Password][Password has been set for user : " || $email || "]"),
update:output(<result><status>Success</status><name>{$username}</name><message>Password has been reset</message></result>)
)
else
(
update:output(<result><status>Failure</status><message>The password token has expired</message></result>),
config:update-message("[Check Password][The password token has expired : " || $token || "]")
)
}
catch *
{
admin:write-log("[Set Password][Error: " || $err:description || "]"),
admin:write-log("[Set Password][Error: " || $err:additional || "]"),
update:output(<result><status>Error</status><message>{$err:description}</message></result>)
}
};