mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Merge pull request #31 from clems4ever/no-base-url
Remove '/authentication/' base path from endpoint URLs
This commit is contained in:
commit
280c6ca21a
10
.travis.yml
10
.travis.yml
|
@ -8,6 +8,15 @@ addons:
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- libgif-dev
|
- libgif-dev
|
||||||
|
hosts:
|
||||||
|
- auth.test.local
|
||||||
|
- home.test.local
|
||||||
|
- secret.test.local
|
||||||
|
- secret1.test.local
|
||||||
|
- secret2.test.local
|
||||||
|
- mx1.mail.test.local
|
||||||
|
- mx2.mail.test.local
|
||||||
|
|
||||||
before_install: npm install -g npm@'>=2.13.5'
|
before_install: npm install -g npm@'>=2.13.5'
|
||||||
script:
|
script:
|
||||||
- npm test
|
- npm test
|
||||||
|
@ -16,6 +25,7 @@ script:
|
||||||
- docker-compose up -d
|
- docker-compose up -d
|
||||||
- sleep 5
|
- sleep 5
|
||||||
- ./scripts/check_services.sh
|
- ./scripts/check_services.sh
|
||||||
|
- npm run int-test
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- if [ "$TRAVIS_BRANCH" == "master" ]; then
|
- if [ "$TRAVIS_BRANCH" == "master" ]; then
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
You can also log off by visiting the following <a href="https://auth.test.local:8080/authentication/logout?redirect=https://home.test.local:8080/">link</a>.
|
You can also log off by visiting the following <a href="https://auth.test.local:8080/logout?redirect=https://home.test.local:8080/">link</a>.
|
||||||
|
|
||||||
<h1>List of users</h1>
|
<h1>List of users</h1>
|
||||||
Here is the list of credentials you can log in with to test access control.
|
Here is the list of credentials you can log in with to test access control.
|
||||||
|
@ -46,7 +46,6 @@
|
||||||
<li>home.test.local</li>
|
<li>home.test.local</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li><strong>Groups policy</strong>
|
<li><strong>Groups policy</strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li>admin
|
<li>admin
|
||||||
|
@ -62,7 +61,6 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li><strong>Users policy</strong>
|
<li><strong>Users policy</strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li>harry
|
<li>harry
|
||||||
|
@ -78,6 +76,5 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -32,26 +32,26 @@ http {
|
||||||
|
|
||||||
error_page 401 = @error401;
|
error_page 401 = @error401;
|
||||||
location @error401 {
|
location @error401 {
|
||||||
return 302 https://auth.test.local:8080/authentication/login?redirect=$scheme://$http_host$request_uri;
|
return 302 https://auth.test.local:8080/login?redirect=$scheme://$http_host$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /authentication/ {
|
location / {
|
||||||
proxy_set_header X-Original-URI $request_uri;
|
proxy_set_header X-Original-URI $request_uri;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|
||||||
proxy_pass http://auth/authentication/;
|
proxy_pass http://auth/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /authentication/js/ {
|
location /js/ {
|
||||||
proxy_pass http://auth/js/;
|
proxy_pass http://auth/js/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /authentication/img/ {
|
location /img/ {
|
||||||
proxy_pass http://auth/img/;
|
proxy_pass http://auth/img/;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /authentication/css/ {
|
location /css/ {
|
||||||
proxy_pass http://auth/css/;
|
proxy_pass http://auth/css/;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,19 +70,20 @@ http {
|
||||||
|
|
||||||
error_page 401 = @error401;
|
error_page 401 = @error401;
|
||||||
location @error401 {
|
location @error401 {
|
||||||
return 302 https://auth.test.local:8080/authentication/login?redirect=$scheme://$http_host$request_uri;
|
return 302 https://auth.test.local:8080/login?redirect=$scheme://$http_host$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /authentication/verify {
|
location /auth_verify {
|
||||||
|
internal;
|
||||||
proxy_set_header X-Original-URI $request_uri;
|
proxy_set_header X-Original-URI $request_uri;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
|
|
||||||
proxy_pass http://auth/authentication/verify;
|
proxy_pass http://auth/verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = /secret.html {
|
location = /secret.html {
|
||||||
auth_request /authentication/verify;
|
auth_request /auth_verify;
|
||||||
|
|
||||||
auth_request_set $user $upstream_http_x_remote_user;
|
auth_request_set $user $upstream_http_x_remote_user;
|
||||||
proxy_set_header X-Forwarded-User $user;
|
proxy_set_header X-Forwarded-User $user;
|
||||||
|
|
|
@ -5,8 +5,6 @@ var routes = require('./routes');
|
||||||
var identity_check = require('./identity_check');
|
var identity_check = require('./identity_check');
|
||||||
|
|
||||||
function setup_endpoints(app) {
|
function setup_endpoints(app) {
|
||||||
var base_endpoint = '/authentication';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @apiDefine UserSession
|
* @apiDefine UserSession
|
||||||
* @apiHeader {String} Cookie Cookie containing 'connect.sid', the user
|
* @apiHeader {String} Cookie Cookie containing 'connect.sid', the user
|
||||||
|
@ -41,7 +39,7 @@ function setup_endpoints(app) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/login Serve login page
|
* @api {get} /login Serve login page
|
||||||
* @apiName Login
|
* @apiName Login
|
||||||
* @apiGroup Pages
|
* @apiGroup Pages
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -52,10 +50,10 @@ function setup_endpoints(app) {
|
||||||
* @apiDescription Create a user session and serve the login page along with
|
* @apiDescription Create a user session and serve the login page along with
|
||||||
* a cookie.
|
* a cookie.
|
||||||
*/
|
*/
|
||||||
app.get (base_endpoint + '/login', routes.login);
|
app.get ('/login', routes.login);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/logout Server logout page
|
* @api {get} /logout Server logout page
|
||||||
* @apiName Logout
|
* @apiName Logout
|
||||||
* @apiGroup Pages
|
* @apiGroup Pages
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -65,10 +63,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Deauthenticate the user and redirect him.
|
* @apiDescription Deauthenticate the user and redirect him.
|
||||||
*/
|
*/
|
||||||
app.get (base_endpoint + '/logout', routes.logout);
|
app.get ('/logout', routes.logout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/totp-register Request TOTP registration
|
* @api {post} /totp-register Request TOTP registration
|
||||||
* @apiName RequestTOTPRegistration
|
* @apiName RequestTOTPRegistration
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -76,7 +74,7 @@ function setup_endpoints(app) {
|
||||||
* @apiUse IdentityValidationPost
|
* @apiUse IdentityValidationPost
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/totp-register Serve TOTP registration page
|
* @api {get} /totp-register Serve TOTP registration page
|
||||||
* @apiName ServeTOTPRegistrationPage
|
* @apiName ServeTOTPRegistrationPage
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -87,11 +85,11 @@ function setup_endpoints(app) {
|
||||||
* @apiDescription Serves the TOTP registration page that displays the secret.
|
* @apiDescription Serves the TOTP registration page that displays the secret.
|
||||||
* The secret is a QRCode and a base32 secret.
|
* The secret is a QRCode and a base32 secret.
|
||||||
*/
|
*/
|
||||||
identity_check(app, base_endpoint + '/totp-register', routes.totp_register.icheck_interface);
|
identity_check(app, '/totp-register', routes.totp_register.icheck_interface);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/u2f-register Request U2F registration
|
* @api {post} /u2f-register Request U2F registration
|
||||||
* @apiName RequestU2FRegistration
|
* @apiName RequestU2FRegistration
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -99,7 +97,7 @@ function setup_endpoints(app) {
|
||||||
* @apiUse IdentityValidationPost
|
* @apiUse IdentityValidationPost
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/u2f-register Serve U2F registration page
|
* @api {get} /u2f-register Serve U2F registration page
|
||||||
* @apiName ServeU2FRegistrationPage
|
* @apiName ServeU2FRegistrationPage
|
||||||
* @apiGroup Pages
|
* @apiGroup Pages
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -109,10 +107,10 @@ function setup_endpoints(app) {
|
||||||
* @apiDescription Serves the U2F registration page that asks the user to
|
* @apiDescription Serves the U2F registration page that asks the user to
|
||||||
* touch the token of the U2F device.
|
* touch the token of the U2F device.
|
||||||
*/
|
*/
|
||||||
identity_check(app, base_endpoint + '/u2f-register', routes.u2f_register.icheck_interface);
|
identity_check(app, '/u2f-register', routes.u2f_register.icheck_interface);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/reset-password Request for password reset
|
* @api {post} /reset-password Request for password reset
|
||||||
* @apiName RequestPasswordReset
|
* @apiName RequestPasswordReset
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -120,7 +118,7 @@ function setup_endpoints(app) {
|
||||||
* @apiUse IdentityValidationPost
|
* @apiUse IdentityValidationPost
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/reset-password Serve password reset form.
|
* @api {get} /reset-password Serve password reset form.
|
||||||
* @apiName ServePasswordResetForm
|
* @apiName ServePasswordResetForm
|
||||||
* @apiGroup Pages
|
* @apiGroup Pages
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -130,12 +128,12 @@ function setup_endpoints(app) {
|
||||||
* @apiDescription Serves password reset form that allow the user to provide
|
* @apiDescription Serves password reset form that allow the user to provide
|
||||||
* the new password.
|
* the new password.
|
||||||
*/
|
*/
|
||||||
identity_check(app, base_endpoint + '/reset-password', routes.reset_password.icheck_interface);
|
identity_check(app, '/reset-password', routes.reset_password.icheck_interface);
|
||||||
|
|
||||||
app.get (base_endpoint + '/reset-password-form', function(req, res) { res.render('reset-password-form'); });
|
app.get ('/reset-password-form', function(req, res) { res.render('reset-password-form'); });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/new-password Set LDAP password
|
* @api {post} /new-password Set LDAP password
|
||||||
* @apiName SetLDAPPassword
|
* @apiName SetLDAPPassword
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -145,10 +143,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Set a new password for the user.
|
* @apiDescription Set a new password for the user.
|
||||||
*/
|
*/
|
||||||
app.post (base_endpoint + '/new-password', routes.reset_password.post);
|
app.post ('/new-password', routes.reset_password.post);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/new-totp-secret Generate TOTP secret
|
* @api {post} /new-totp-secret Generate TOTP secret
|
||||||
* @apiName GenerateTOTPSecret
|
* @apiName GenerateTOTPSecret
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -164,10 +162,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Generate a new TOTP secret and returns it.
|
* @apiDescription Generate a new TOTP secret and returns it.
|
||||||
*/
|
*/
|
||||||
app.post (base_endpoint + '/new-totp-secret', routes.totp_register.post);
|
app.post ('/new-totp-secret', routes.totp_register.post);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/verify Verify user authentication
|
* @api {get} /verify Verify user authentication
|
||||||
* @apiName VerifyAuthentication
|
* @apiName VerifyAuthentication
|
||||||
* @apiGroup Verification
|
* @apiGroup Verification
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -179,10 +177,10 @@ function setup_endpoints(app) {
|
||||||
* @apiDescription Verify that the user is authenticated, i.e., the two
|
* @apiDescription Verify that the user is authenticated, i.e., the two
|
||||||
* factors have been validated
|
* factors have been validated
|
||||||
*/
|
*/
|
||||||
app.get (base_endpoint + '/verify', routes.verify);
|
app.get ('/verify', routes.verify);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/1stfactor LDAP authentication
|
* @api {post} /1stfactor LDAP authentication
|
||||||
* @apiName ValidateFirstFactor
|
* @apiName ValidateFirstFactor
|
||||||
* @apiGroup Authentication
|
* @apiGroup Authentication
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -199,10 +197,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Verify credentials against the LDAP.
|
* @apiDescription Verify credentials against the LDAP.
|
||||||
*/
|
*/
|
||||||
app.post (base_endpoint + '/1stfactor', routes.first_factor);
|
app.post ('/1stfactor', routes.first_factor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/2ndfactor/totp TOTP authentication
|
* @api {post} /2ndfactor/totp TOTP authentication
|
||||||
* @apiName ValidateTOTPSecondFactor
|
* @apiName ValidateTOTPSecondFactor
|
||||||
* @apiGroup Authentication
|
* @apiGroup Authentication
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -216,10 +214,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Verify TOTP token. The user is authenticated upon success.
|
* @apiDescription Verify TOTP token. The user is authenticated upon success.
|
||||||
*/
|
*/
|
||||||
app.post (base_endpoint + '/2ndfactor/totp', routes.second_factor.totp);
|
app.post ('/2ndfactor/totp', routes.second_factor.totp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/2ndfactor/u2f/sign_request U2F Start authentication
|
* @api {get} /2ndfactor/u2f/sign_request U2F Start authentication
|
||||||
* @apiName StartU2FAuthentication
|
* @apiName StartU2FAuthentication
|
||||||
* @apiGroup Authentication
|
* @apiGroup Authentication
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -231,10 +229,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Initiate an authentication request using a U2F device.
|
* @apiDescription Initiate an authentication request using a U2F device.
|
||||||
*/
|
*/
|
||||||
app.get (base_endpoint + '/2ndfactor/u2f/sign_request', routes.second_factor.u2f.sign_request);
|
app.get ('/2ndfactor/u2f/sign_request', routes.second_factor.u2f.sign_request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/2ndfactor/u2f/sign U2F Complete authentication
|
* @api {post} /2ndfactor/u2f/sign U2F Complete authentication
|
||||||
* @apiName CompleteU2FAuthentication
|
* @apiName CompleteU2FAuthentication
|
||||||
* @apiGroup Authentication
|
* @apiGroup Authentication
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -246,10 +244,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Complete authentication request of the U2F device.
|
* @apiDescription Complete authentication request of the U2F device.
|
||||||
*/
|
*/
|
||||||
app.post (base_endpoint + '/2ndfactor/u2f/sign', routes.second_factor.u2f.sign);
|
app.post ('/2ndfactor/u2f/sign', routes.second_factor.u2f.sign);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} /authentication/2ndfactor/u2f/register_request U2F Start device registration
|
* @api {get} /2ndfactor/u2f/register_request U2F Start device registration
|
||||||
* @apiName StartU2FRegistration
|
* @apiName StartU2FRegistration
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -261,10 +259,10 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Initiate a U2F device registration request.
|
* @apiDescription Initiate a U2F device registration request.
|
||||||
*/
|
*/
|
||||||
app.get (base_endpoint + '/2ndfactor/u2f/register_request', routes.second_factor.u2f.register_request);
|
app.get ('/2ndfactor/u2f/register_request', routes.second_factor.u2f.register_request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /authentication/2ndfactor/u2f/register U2F Complete device registration
|
* @api {post} /2ndfactor/u2f/register U2F Complete device registration
|
||||||
* @apiName CompleteU2FRegistration
|
* @apiName CompleteU2FRegistration
|
||||||
* @apiGroup Registration
|
* @apiGroup Registration
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -277,6 +275,6 @@ function setup_endpoints(app) {
|
||||||
*
|
*
|
||||||
* @apiDescription Complete U2F registration request.
|
* @apiDescription Complete U2F registration request.
|
||||||
*/
|
*/
|
||||||
app.post (base_endpoint + '/2ndfactor/u2f/register', routes.second_factor.u2f.register);
|
app.post ('/2ndfactor/u2f/register', routes.second_factor.u2f.register);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ function onLoginButtonClicked() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onResetPasswordButtonClicked() {
|
function onResetPasswordButtonClicked() {
|
||||||
var r = '/authentication/reset-password-form';
|
var r = '/reset-password-form';
|
||||||
window.location.replace(r);
|
window.location.replace(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ function onTotpSignButtonClicked() {
|
||||||
function onTotpRegisterButtonClicked() {
|
function onTotpRegisterButtonClicked() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/authentication/totp-register',
|
url: '/totp-register',
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
redirect: get_redirect_param()
|
redirect: get_redirect_param()
|
||||||
}),
|
}),
|
||||||
|
@ -92,7 +92,7 @@ function onU2fRegistrationButtonClicked() {
|
||||||
function askForU2fRegistration(fn) {
|
function askForU2fRegistration(fn) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/authentication/u2f-register',
|
url: '/u2f-register',
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
redirect: get_redirect_param()
|
redirect: get_redirect_param()
|
||||||
}),
|
}),
|
||||||
|
@ -124,7 +124,7 @@ function finishU2fAuthentication(url, responseData, fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function startU2fAuthentication(fn, timeout) {
|
function startU2fAuthentication(fn, timeout) {
|
||||||
$.get('/authentication/2ndfactor/u2f/sign_request', {}, null, 'json')
|
$.get('/2ndfactor/u2f/sign_request', {}, null, 'json')
|
||||||
.done(function(signResponse) {
|
.done(function(signResponse) {
|
||||||
var registeredKeys = signResponse.registeredKeys;
|
var registeredKeys = signResponse.registeredKeys;
|
||||||
$.notify('Please touch the token', 'info');
|
$.notify('Please touch the token', 'info');
|
||||||
|
@ -137,7 +137,7 @@ function startU2fAuthentication(fn, timeout) {
|
||||||
if (response.errorCode) {
|
if (response.errorCode) {
|
||||||
fn(response);
|
fn(response);
|
||||||
} else {
|
} else {
|
||||||
finishU2fAuthentication('/authentication/2ndfactor/u2f/sign', response, fn);
|
finishU2fAuthentication('/2ndfactor/u2f/sign', response, fn);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
timeout
|
timeout
|
||||||
|
@ -149,7 +149,7 @@ function startU2fAuthentication(fn, timeout) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateSecondFactorTotp(token, fn) {
|
function validateSecondFactorTotp(token, fn) {
|
||||||
$.post('/authentication/2ndfactor/totp', {
|
$.post('/2ndfactor/totp', {
|
||||||
token: token,
|
token: token,
|
||||||
})
|
})
|
||||||
.done(function() {
|
.done(function() {
|
||||||
|
@ -161,7 +161,7 @@ function validateSecondFactorTotp(token, fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateFirstFactor(username, password, fn) {
|
function validateFirstFactor(username, password, fn) {
|
||||||
$.post('/authentication/1stfactor', {
|
$.post('/1stfactor', {
|
||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,13 +21,13 @@ function onResetPasswordButtonClicked() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$.post('/authentication/reset-password', {
|
$.post('/reset-password', {
|
||||||
userid: username,
|
userid: username,
|
||||||
})
|
})
|
||||||
.done(function() {
|
.done(function() {
|
||||||
$.notify('An email has been sent. Click on the link to change your password', 'success');
|
$.notify('An email has been sent. Click on the link to change your password', 'success');
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
window.location.replace('/authentication/login');
|
window.location.replace('/login');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
})
|
})
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
|
|
|
@ -27,12 +27,12 @@ function onResetPasswordButtonClicked() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$.post('/authentication/new-password', {
|
$.post('/new-password', {
|
||||||
password: password1,
|
password: password1,
|
||||||
})
|
})
|
||||||
.done(function() {
|
.done(function() {
|
||||||
$.notify('Your password has been changed. Please login again', 'success');
|
$.notify('Your password has been changed. Please login again', 'success');
|
||||||
window.location.replace('/authentication/login');
|
window.location.replace('/login');
|
||||||
})
|
})
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
$.notify('An error occurred during password change.', 'warn');
|
$.notify('An error occurred during password change.', 'warn');
|
||||||
|
|
|
@ -6,7 +6,7 @@ location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(s,k,v){params[k]=v});
|
||||||
function generateSecret(fn) {
|
function generateSecret(fn) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/authentication/new-totp-secret',
|
url: '/new-totp-secret',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
})
|
})
|
||||||
|
@ -26,7 +26,7 @@ function onSecretGenerated(err, secret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function redirect() {
|
function redirect() {
|
||||||
var redirect_uri = '/authentication/login';
|
var redirect_uri = '/login';
|
||||||
if('redirect' in params) {
|
if('redirect' in params) {
|
||||||
redirect_uri = params['redirect'];
|
redirect_uri = params['redirect'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ function finishRegister(url, responseData, fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function startRegister(fn, timeout) {
|
function startRegister(fn, timeout) {
|
||||||
$.get('/authentication/2ndfactor/u2f/register_request', {}, null, 'json')
|
$.get('/2ndfactor/u2f/register_request', {}, null, 'json')
|
||||||
.done(function(startRegisterResponse) {
|
.done(function(startRegisterResponse) {
|
||||||
u2f.register(
|
u2f.register(
|
||||||
startRegisterResponse.appId,
|
startRegisterResponse.appId,
|
||||||
|
@ -30,7 +30,7 @@ function startRegister(fn, timeout) {
|
||||||
if (response.errorCode) {
|
if (response.errorCode) {
|
||||||
fn(response.errorCode);
|
fn(response.errorCode);
|
||||||
} else {
|
} else {
|
||||||
finishRegister('/authentication/2ndfactor/u2f/register', response, fn);
|
finishRegister('/2ndfactor/u2f/register', response, fn);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
timeout
|
timeout
|
||||||
|
@ -39,7 +39,7 @@ function startRegister(fn, timeout) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function redirect() {
|
function redirect() {
|
||||||
var redirect_uri = '/authentication/login';
|
var redirect_uri = '/login';
|
||||||
if('redirect' in params) {
|
if('redirect' in params) {
|
||||||
redirect_uri = params['redirect'];
|
redirect_uri = params['redirect'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,13 @@ var AUTHELIA_HOST = 'nginx';
|
||||||
var DOMAIN = 'test.local';
|
var DOMAIN = 'test.local';
|
||||||
var PORT = 8080;
|
var PORT = 8080;
|
||||||
|
|
||||||
var BASE_URL = util.format('https://%s.%s:%d', 'home', DOMAIN, PORT);
|
var HOME_URL = util.format('https://%s.%s:%d', 'home', DOMAIN, PORT);
|
||||||
var BASE_AUTH_URL = util.format('https://%s.%s:%d/authentication', 'auth', DOMAIN, PORT);
|
var SECRET_URL = util.format('https://%s.%s:%d', 'secret', DOMAIN, PORT);
|
||||||
|
var SECRET1_URL = util.format('https://%s.%s:%d', 'secret1', DOMAIN, PORT);
|
||||||
|
var SECRET2_URL = util.format('https://%s.%s:%d', 'secret2', DOMAIN, PORT);
|
||||||
|
var MX1_URL = util.format('https://%s.%s:%d', 'mx1.mail', DOMAIN, PORT);
|
||||||
|
var MX2_URL = util.format('https://%s.%s:%d', 'mx2.mail', DOMAIN, PORT);
|
||||||
|
var BASE_AUTH_URL = util.format('https://%s.%s:%d', 'auth', DOMAIN, PORT);
|
||||||
|
|
||||||
describe('test the server', function() {
|
describe('test the server', function() {
|
||||||
var home_page;
|
var home_page;
|
||||||
|
@ -34,6 +39,24 @@ describe('test the server', function() {
|
||||||
login_page_promise]);
|
login_page_promise]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function str_contains(str, pattern) {
|
||||||
|
return str.indexOf(pattern) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function home_page_contains(pattern) {
|
||||||
|
return str_contains(home_page, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should serve a correct home page', function() {
|
||||||
|
assert(home_page_contains(BASE_AUTH_URL + '/logout?redirect=' + HOME_URL + '/'));
|
||||||
|
assert(home_page_contains(HOME_URL + '/secret.html'));
|
||||||
|
assert(home_page_contains(SECRET_URL + '/secret.html'));
|
||||||
|
assert(home_page_contains(SECRET1_URL + '/secret.html'));
|
||||||
|
assert(home_page_contains(SECRET2_URL + '/secret.html'));
|
||||||
|
assert(home_page_contains(MX1_URL + '/secret.html'));
|
||||||
|
assert(home_page_contains(MX2_URL + '/secret.html'));
|
||||||
|
});
|
||||||
|
|
||||||
it('should serve the login page', function(done) {
|
it('should serve the login page', function(done) {
|
||||||
getPromised(BASE_AUTH_URL + '/login?redirect=/')
|
getPromised(BASE_AUTH_URL + '/login?redirect=/')
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
|
@ -43,7 +66,7 @@ describe('test the server', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serve the homepage', function(done) {
|
it('should serve the homepage', function(done) {
|
||||||
getPromised(BASE_URL + '/')
|
getPromised(HOME_URL + '/')
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
assert.equal(data.statusCode, 200);
|
assert.equal(data.statusCode, 200);
|
||||||
done();
|
done();
|
||||||
|
@ -51,7 +74,7 @@ describe('test the server', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect when logout', function(done) {
|
it('should redirect when logout', function(done) {
|
||||||
getPromised(BASE_AUTH_URL + '/logout?redirect=' + BASE_URL)
|
getPromised(BASE_AUTH_URL + '/logout?redirect=' + HOME_URL)
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
assert.equal(data.statusCode, 200);
|
assert.equal(data.statusCode, 200);
|
||||||
assert.equal(data.body, home_page);
|
assert.equal(data.body, home_page);
|
||||||
|
@ -60,7 +83,7 @@ describe('test the server', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be redirected to the login page when accessing secret while not authenticated', function(done) {
|
it('should be redirected to the login page when accessing secret while not authenticated', function(done) {
|
||||||
var url = BASE_URL + '/secret.html';
|
var url = HOME_URL + '/secret.html';
|
||||||
// console.log(url);
|
// console.log(url);
|
||||||
getPromised(url)
|
getPromised(url)
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
|
@ -125,7 +148,7 @@ function postPromised(url, body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHomePage() {
|
function getHomePage() {
|
||||||
return getPromised(BASE_URL + '/');
|
return getPromised(HOME_URL + '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLoginPage() {
|
function getLoginPage() {
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = function(port) {
|
||||||
|
|
||||||
function execute_reset_password(jar, transporter, user, new_password) {
|
function execute_reset_password(jar, transporter, user, new_password) {
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/reset-password',
|
url: BASE_URL + '/reset-password',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
form: { userid: user }
|
form: { userid: user }
|
||||||
})
|
})
|
||||||
|
@ -20,14 +20,14 @@ module.exports = function(port) {
|
||||||
var token = regexp.exec(html_content)[1];
|
var token = regexp.exec(html_content)[1];
|
||||||
// console.log(html_content, token);
|
// console.log(html_content, token);
|
||||||
return request.getAsync({
|
return request.getAsync({
|
||||||
url: BASE_URL + '/authentication/reset-password?identity_token=' + token,
|
url: BASE_URL + '/reset-password?identity_token=' + token,
|
||||||
jar: jar
|
jar: jar
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
assert.equal(res.statusCode, 200);
|
assert.equal(res.statusCode, 200);
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/new-password',
|
url: BASE_URL + '/new-password',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
form: {
|
form: {
|
||||||
password: new_password
|
password: new_password
|
||||||
|
@ -38,7 +38,7 @@ module.exports = function(port) {
|
||||||
|
|
||||||
function execute_register_totp(jar, transporter) {
|
function execute_register_totp(jar, transporter) {
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/totp-register',
|
url: BASE_URL + '/totp-register',
|
||||||
jar: jar
|
jar: jar
|
||||||
})
|
})
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
|
@ -48,14 +48,14 @@ module.exports = function(port) {
|
||||||
var token = regexp.exec(html_content)[1];
|
var token = regexp.exec(html_content)[1];
|
||||||
// console.log(html_content, token);
|
// console.log(html_content, token);
|
||||||
return request.getAsync({
|
return request.getAsync({
|
||||||
url: BASE_URL + '/authentication/totp-register?identity_token=' + token,
|
url: BASE_URL + '/totp-register?identity_token=' + token,
|
||||||
jar: jar
|
jar: jar
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
assert.equal(res.statusCode, 200);
|
assert.equal(res.statusCode, 200);
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url : BASE_URL + '/authentication/new-totp-secret',
|
url : BASE_URL + '/new-totp-secret',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -69,7 +69,7 @@ module.exports = function(port) {
|
||||||
|
|
||||||
function execute_totp(jar, token) {
|
function execute_totp(jar, token) {
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/2ndfactor/totp',
|
url: BASE_URL + '/2ndfactor/totp',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
form: {
|
form: {
|
||||||
token: token
|
token: token
|
||||||
|
@ -79,13 +79,13 @@ module.exports = function(port) {
|
||||||
|
|
||||||
function execute_u2f_authentication(jar) {
|
function execute_u2f_authentication(jar) {
|
||||||
return request.getAsync({
|
return request.getAsync({
|
||||||
url: BASE_URL + '/authentication/2ndfactor/u2f/sign_request',
|
url: BASE_URL + '/2ndfactor/u2f/sign_request',
|
||||||
jar: jar
|
jar: jar
|
||||||
})
|
})
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
assert.equal(res.statusCode, 200);
|
assert.equal(res.statusCode, 200);
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/2ndfactor/u2f/sign',
|
url: BASE_URL + '/2ndfactor/u2f/sign',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
form: {
|
form: {
|
||||||
}
|
}
|
||||||
|
@ -94,16 +94,16 @@ module.exports = function(port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute_verification(jar) {
|
function execute_verification(jar) {
|
||||||
return request.getAsync({ url: BASE_URL + '/authentication/verify', jar: jar })
|
return request.getAsync({ url: BASE_URL + '/verify', jar: jar })
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute_login(jar) {
|
function execute_login(jar) {
|
||||||
return request.getAsync({ url: BASE_URL + '/authentication/login', jar: jar })
|
return request.getAsync({ url: BASE_URL + '/login', jar: jar })
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute_u2f_registration(jar, transporter) {
|
function execute_u2f_registration(jar, transporter) {
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/u2f-register',
|
url: BASE_URL + '/u2f-register',
|
||||||
jar: jar
|
jar: jar
|
||||||
})
|
})
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
|
@ -113,21 +113,21 @@ module.exports = function(port) {
|
||||||
var token = regexp.exec(html_content)[1];
|
var token = regexp.exec(html_content)[1];
|
||||||
// console.log(html_content, token);
|
// console.log(html_content, token);
|
||||||
return request.getAsync({
|
return request.getAsync({
|
||||||
url: BASE_URL + '/authentication/u2f-register?identity_token=' + token,
|
url: BASE_URL + '/u2f-register?identity_token=' + token,
|
||||||
jar: jar
|
jar: jar
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
assert.equal(res.statusCode, 200);
|
assert.equal(res.statusCode, 200);
|
||||||
return request.getAsync({
|
return request.getAsync({
|
||||||
url: BASE_URL + '/authentication/2ndfactor/u2f/register_request',
|
url: BASE_URL + '/2ndfactor/u2f/register_request',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
assert.equal(res.statusCode, 200);
|
assert.equal(res.statusCode, 200);
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/2ndfactor/u2f/register',
|
url: BASE_URL + '/2ndfactor/u2f/register',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
form: {
|
form: {
|
||||||
s: 'test'
|
s: 'test'
|
||||||
|
@ -138,7 +138,7 @@ module.exports = function(port) {
|
||||||
|
|
||||||
function execute_first_factor(jar) {
|
function execute_first_factor(jar) {
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/1stfactor',
|
url: BASE_URL + '/1stfactor',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
form: {
|
form: {
|
||||||
username: 'test_ok',
|
username: 'test_ok',
|
||||||
|
@ -149,7 +149,7 @@ module.exports = function(port) {
|
||||||
|
|
||||||
function execute_failing_first_factor(jar) {
|
function execute_failing_first_factor(jar) {
|
||||||
return request.postAsync({
|
return request.postAsync({
|
||||||
url: BASE_URL + '/authentication/1stfactor',
|
url: BASE_URL + '/1stfactor',
|
||||||
jar: jar,
|
jar: jar,
|
||||||
form: {
|
form: {
|
||||||
username: 'test_nok',
|
username: 'test_nok',
|
||||||
|
|
|
@ -164,32 +164,32 @@ describe('test the server', function() {
|
||||||
return Promise.all([p1, p2]);
|
return Promise.all([p1, p2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should block /authentication/new-password', function() {
|
it('should block /new-password', function() {
|
||||||
return should_post_and_reply_with_403(BASE_URL + '/authentication/new-password')
|
return should_post_and_reply_with_403(BASE_URL + '/new-password')
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block /authentication/u2f-register', function() {
|
it('should block /u2f-register', function() {
|
||||||
return should_get_and_post_reply_with_403(BASE_URL + '/authentication/u2f-register');
|
return should_get_and_post_reply_with_403(BASE_URL + '/u2f-register');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block /authentication/reset-password', function() {
|
it('should block /reset-password', function() {
|
||||||
return should_get_and_post_reply_with_403(BASE_URL + '/authentication/reset-password');
|
return should_get_and_post_reply_with_403(BASE_URL + '/reset-password');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block /authentication/2ndfactor/u2f/register_request', function() {
|
it('should block /2ndfactor/u2f/register_request', function() {
|
||||||
return should_get_and_reply_with_403(BASE_URL + '/authentication/2ndfactor/u2f/register_request');
|
return should_get_and_reply_with_403(BASE_URL + '/2ndfactor/u2f/register_request');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block /authentication/2ndfactor/u2f/register', function() {
|
it('should block /2ndfactor/u2f/register', function() {
|
||||||
return should_post_and_reply_with_403(BASE_URL + '/authentication/2ndfactor/u2f/register');
|
return should_post_and_reply_with_403(BASE_URL + '/2ndfactor/u2f/register');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block /authentication/2ndfactor/u2f/sign_request', function() {
|
it('should block /2ndfactor/u2f/sign_request', function() {
|
||||||
return should_get_and_reply_with_403(BASE_URL + '/authentication/2ndfactor/u2f/sign_request');
|
return should_get_and_reply_with_403(BASE_URL + '/2ndfactor/u2f/sign_request');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block /authentication/2ndfactor/u2f/sign', function() {
|
it('should block /2ndfactor/u2f/sign', function() {
|
||||||
return should_post_and_reply_with_403(BASE_URL + '/authentication/2ndfactor/u2f/sign');
|
return should_post_and_reply_with_403(BASE_URL + '/2ndfactor/u2f/sign');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ describe('test the server', function() {
|
||||||
|
|
||||||
function test_reset_password_form() {
|
function test_reset_password_form() {
|
||||||
it('should serve the reset password form page', function(done) {
|
it('should serve the reset password form page', function(done) {
|
||||||
request.getAsync(BASE_URL + '/authentication/reset-password-form')
|
request.getAsync(BASE_URL + '/reset-password-form')
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
assert.equal(response.statusCode, 200);
|
assert.equal(response.statusCode, 200);
|
||||||
done();
|
done();
|
||||||
|
@ -211,7 +211,7 @@ describe('test the server', function() {
|
||||||
|
|
||||||
function test_login() {
|
function test_login() {
|
||||||
it('should serve the login page', function(done) {
|
it('should serve the login page', function(done) {
|
||||||
request.getAsync(BASE_URL + '/authentication/login')
|
request.getAsync(BASE_URL + '/login')
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
assert.equal(response.statusCode, 200);
|
assert.equal(response.statusCode, 200);
|
||||||
done();
|
done();
|
||||||
|
@ -221,7 +221,7 @@ describe('test the server', function() {
|
||||||
|
|
||||||
function test_logout() {
|
function test_logout() {
|
||||||
it('should logout and redirect to /', function(done) {
|
it('should logout and redirect to /', function(done) {
|
||||||
request.getAsync(BASE_URL + '/authentication/logout')
|
request.getAsync(BASE_URL + '/logout')
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
assert.equal(response.req.path, '/');
|
assert.equal(response.req.path, '/');
|
||||||
done();
|
done();
|
||||||
|
@ -231,7 +231,7 @@ describe('test the server', function() {
|
||||||
|
|
||||||
function test_authentication() {
|
function test_authentication() {
|
||||||
it('should return status code 401 when user is not authenticated', function() {
|
it('should return status code 401 when user is not authenticated', function() {
|
||||||
return request.getAsync({ url: BASE_URL + '/authentication/verify' })
|
return request.getAsync({ url: BASE_URL + '/verify' })
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
assert.equal(response.statusCode, 401);
|
assert.equal(response.statusCode, 401);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user