mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-10-05 12:20:10 +00:00
Compare commits
16 Commits
v2.12.3
...
2df79f75cb
Author | SHA1 | Date | |
---|---|---|---|
|
2df79f75cb | ||
|
34194e65d2 | ||
|
5d2a76adfe | ||
|
33906bfcdc | ||
|
6e51d819fa | ||
|
8aa173a732 | ||
|
35938db24b | ||
|
69f7920675 | ||
|
45fc63875c | ||
|
f3fee7d886 | ||
|
50f7bfc726 | ||
|
8fb9d9fec6 | ||
|
cea9a17218 | ||
|
6bbe7d4cc4 | ||
|
c6ab315165 | ||
|
2c1595756d |
@@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://nginxproxymanager.com/github.png">
|
<img src="https://nginxproxymanager.com/github.png">
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="https://img.shields.io/badge/version-2.12.3-green.svg?style=for-the-badge">
|
<img src="https://img.shields.io/badge/version-2.12.2-green.svg?style=for-the-badge">
|
||||||
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||||
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||
</a>
|
</a>
|
||||||
|
@@ -508,13 +508,8 @@ const internalAccessList = {
|
|||||||
if (typeof item.password !== 'undefined' && item.password.length) {
|
if (typeof item.password !== 'undefined' && item.password.length) {
|
||||||
logger.info('Adding: ' + item.username);
|
logger.info('Adding: ' + item.username);
|
||||||
|
|
||||||
utils.execFile('openssl', ['passwd', '-apr1', item.password])
|
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
|
||||||
.then((res) => {
|
.then((/*result*/) => {
|
||||||
try {
|
|
||||||
fs.appendFileSync(htpasswd_file, item.username + ':' + res + '\n', {encoding: 'utf8'});
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
next();
|
next();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
76
backend/internal/mfa.js
Normal file
76
backend/internal/mfa.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
const authModel = require('../models/auth');
|
||||||
|
const error = require('../lib/error');
|
||||||
|
const speakeasy = require('speakeasy');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
validateMfaTokenForUser: (userId, token) => {
|
||||||
|
return authModel
|
||||||
|
.query()
|
||||||
|
.where('user_id', userId)
|
||||||
|
.first()
|
||||||
|
.then((auth) => {
|
||||||
|
if (!auth || !auth.mfa_enabled) {
|
||||||
|
throw new error.AuthError('MFA is not enabled for this user.');
|
||||||
|
}
|
||||||
|
const verified = speakeasy.totp.verify({
|
||||||
|
secret: auth.mfa_secret,
|
||||||
|
encoding: 'base32',
|
||||||
|
token: token,
|
||||||
|
window: 2
|
||||||
|
});
|
||||||
|
if (!verified) {
|
||||||
|
throw new error.AuthError('Invalid MFA token.');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isMfaEnabledForUser: (userId) => {
|
||||||
|
return authModel
|
||||||
|
.query()
|
||||||
|
.where('user_id', userId)
|
||||||
|
.first()
|
||||||
|
.then((auth) => {
|
||||||
|
console.log(auth);
|
||||||
|
if (!auth) {
|
||||||
|
throw new error.AuthError('User not found.');
|
||||||
|
}
|
||||||
|
return auth.mfa_enabled === true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createMfaSecretForUser: (userId) => {
|
||||||
|
const secret = speakeasy.generateSecret({ length: 20 });
|
||||||
|
console.log(secret);
|
||||||
|
return authModel
|
||||||
|
.query()
|
||||||
|
.where('user_id', userId)
|
||||||
|
.update({
|
||||||
|
mfa_secret: secret.base32
|
||||||
|
})
|
||||||
|
.then(() => secret);
|
||||||
|
},
|
||||||
|
enableMfaForUser: (userId, token) => {
|
||||||
|
return authModel
|
||||||
|
.query()
|
||||||
|
.where('user_id', userId)
|
||||||
|
.first()
|
||||||
|
.then((auth) => {
|
||||||
|
if (!auth || !auth.mfa_secret) {
|
||||||
|
throw new error.AuthError('MFA is not set up for this user.');
|
||||||
|
}
|
||||||
|
const verified = speakeasy.totp.verify({
|
||||||
|
secret: auth.mfa_secret,
|
||||||
|
encoding: 'base32',
|
||||||
|
token: token,
|
||||||
|
window: 2
|
||||||
|
});
|
||||||
|
if (!verified) {
|
||||||
|
throw new error.AuthError('Invalid MFA token.');
|
||||||
|
}
|
||||||
|
return authModel
|
||||||
|
.query()
|
||||||
|
.where('user_id', userId)
|
||||||
|
.update({ mfa_enabled: true })
|
||||||
|
.then(() => true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
@@ -4,6 +4,7 @@ const userModel = require('../models/user');
|
|||||||
const authModel = require('../models/auth');
|
const authModel = require('../models/auth');
|
||||||
const helpers = require('../lib/helpers');
|
const helpers = require('../lib/helpers');
|
||||||
const TokenModel = require('../models/token');
|
const TokenModel = require('../models/token');
|
||||||
|
const mfa = require('../internal/mfa'); // <-- added MFA import
|
||||||
|
|
||||||
const ERROR_MESSAGE_INVALID_AUTH = 'Invalid email or password';
|
const ERROR_MESSAGE_INVALID_AUTH = 'Invalid email or password';
|
||||||
|
|
||||||
@@ -21,6 +22,8 @@ module.exports = {
|
|||||||
getTokenFromEmail: (data, issuer) => {
|
getTokenFromEmail: (data, issuer) => {
|
||||||
let Token = new TokenModel();
|
let Token = new TokenModel();
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
data.scope = data.scope || 'user';
|
data.scope = data.scope || 'user';
|
||||||
data.expiry = data.expiry || '1d';
|
data.expiry = data.expiry || '1d';
|
||||||
|
|
||||||
@@ -41,34 +44,66 @@ module.exports = {
|
|||||||
.then((auth) => {
|
.then((auth) => {
|
||||||
if (auth) {
|
if (auth) {
|
||||||
return auth.verifyPassword(data.secret)
|
return auth.verifyPassword(data.secret)
|
||||||
.then((valid) => {
|
.then(async (valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
|
||||||
if (data.scope !== 'user' && _.indexOf(user.roles, data.scope) === -1) {
|
if (data.scope !== 'user' && _.indexOf(user.roles, data.scope) === -1) {
|
||||||
// The scope requested doesn't exist as a role against the user,
|
|
||||||
// you shall not pass.
|
|
||||||
throw new error.AuthError('Invalid scope: ' + data.scope);
|
throw new error.AuthError('Invalid scope: ' + data.scope);
|
||||||
}
|
}
|
||||||
|
return await mfa.isMfaEnabledForUser(user.id)
|
||||||
|
.then((mfaEnabled) => {
|
||||||
|
if (mfaEnabled) {
|
||||||
|
if (!data.mfa_token) {
|
||||||
|
throw new error.AuthError('MFA token required');
|
||||||
|
}
|
||||||
|
console.log(data.mfa_token);
|
||||||
|
return mfa.validateMfaTokenForUser(user.id, data.mfa_token)
|
||||||
|
.then((mfaValid) => {
|
||||||
|
if (!mfaValid) {
|
||||||
|
throw new error.AuthError('Invalid MFA token');
|
||||||
|
}
|
||||||
|
// Create a moment of the expiry expression
|
||||||
|
let expiry = helpers.parseDatePeriod(data.expiry);
|
||||||
|
if (expiry === null) {
|
||||||
|
throw new error.AuthError('Invalid expiry time: ' + data.expiry);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a moment of the expiry expression
|
return Token.create({
|
||||||
let expiry = helpers.parseDatePeriod(data.expiry);
|
iss: issuer || 'api',
|
||||||
if (expiry === null) {
|
attrs: {
|
||||||
throw new error.AuthError('Invalid expiry time: ' + data.expiry);
|
id: user.id
|
||||||
}
|
},
|
||||||
|
scope: [data.scope],
|
||||||
|
expiresIn: data.expiry
|
||||||
|
})
|
||||||
|
.then((signed) => {
|
||||||
|
return {
|
||||||
|
token: signed.token,
|
||||||
|
expires: expiry.toISOString()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Create a moment of the expiry expression
|
||||||
|
let expiry = helpers.parseDatePeriod(data.expiry);
|
||||||
|
if (expiry === null) {
|
||||||
|
throw new error.AuthError('Invalid expiry time: ' + data.expiry);
|
||||||
|
}
|
||||||
|
|
||||||
return Token.create({
|
return Token.create({
|
||||||
iss: issuer || 'api',
|
iss: issuer || 'api',
|
||||||
attrs: {
|
attrs: {
|
||||||
id: user.id
|
id: user.id
|
||||||
},
|
},
|
||||||
scope: [data.scope],
|
scope: [data.scope],
|
||||||
expiresIn: data.expiry
|
expiresIn: data.expiry
|
||||||
})
|
})
|
||||||
.then((signed) => {
|
.then((signed) => {
|
||||||
return {
|
return {
|
||||||
token: signed.token,
|
token: signed.token,
|
||||||
expires: expiry.toISOString()
|
expires: expiry.toISOString()
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||||
|
@@ -507,7 +507,8 @@ const internalUser = {
|
|||||||
.then((user) => {
|
.then((user) => {
|
||||||
return internalToken.getTokenFromUser(user);
|
return internalToken.getTokenFromUser(user);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = internalUser;
|
module.exports = internalUser;
|
||||||
|
45
backend/migrations/20250115041439_mfa_integeration.js
Normal file
45
backend/migrations/20250115041439_mfa_integeration.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
const migrate_name = 'identifier_for_migrate';
|
||||||
|
const logger = require('../logger').migrate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate
|
||||||
|
*
|
||||||
|
* @see http://knexjs.org/#Schema
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @param {Promise} Promise
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.up = function (knex/*, Promise*/) {
|
||||||
|
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||||
|
|
||||||
|
return knex.schema.alterTable('auth', (table) => {
|
||||||
|
table.string('mfa_secret');
|
||||||
|
table.boolean('mfa_enabled').defaultTo(false);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.info('[' + migrate_name + '] User Table altered');
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Up Complete');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo Migrate
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @param {Promise} Promise
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.down = function (knex/*, Promise*/) {
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Down...');
|
||||||
|
|
||||||
|
return knex.schema.alterTable('auth', (table) => {
|
||||||
|
table.dropColumn('mfa_key');
|
||||||
|
table.dropColumn('mfa_enabled');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.info('[' + migrate_name + '] User Table altered');
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Down Complete');
|
||||||
|
});
|
||||||
|
};
|
@@ -23,8 +23,10 @@
|
|||||||
"node-rsa": "^1.0.8",
|
"node-rsa": "^1.0.8",
|
||||||
"objection": "3.0.1",
|
"objection": "3.0.1",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"pg": "^8.13.1",
|
"pg": "^8.13.1",
|
||||||
"signale": "1.4.0",
|
"signale": "1.4.0",
|
||||||
|
"speakeasy": "^2.0.0",
|
||||||
"sqlite3": "5.1.6",
|
"sqlite3": "5.1.6",
|
||||||
"temp-write": "^4.0.0"
|
"temp-write": "^4.0.0"
|
||||||
},
|
},
|
||||||
|
@@ -27,6 +27,7 @@ router.get('/', (req, res/*, next*/) => {
|
|||||||
|
|
||||||
router.use('/schema', require('./schema'));
|
router.use('/schema', require('./schema'));
|
||||||
router.use('/tokens', require('./tokens'));
|
router.use('/tokens', require('./tokens'));
|
||||||
|
router.use('/mfa', require('./mfa'));
|
||||||
router.use('/users', require('./users'));
|
router.use('/users', require('./users'));
|
||||||
router.use('/audit-log', require('./audit-log'));
|
router.use('/audit-log', require('./audit-log'));
|
||||||
router.use('/reports', require('./reports'));
|
router.use('/reports', require('./reports'));
|
||||||
|
89
backend/routes/mfa.js
Normal file
89
backend/routes/mfa.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const jwtdecode = require('../lib/express/jwt-decode');
|
||||||
|
const apiValidator = require('../lib/validator/api');
|
||||||
|
const internalToken = require('../internal/token');
|
||||||
|
const schema = require('../schema');
|
||||||
|
const internalMfa = require('../internal/mfa');
|
||||||
|
const qrcode = require('qrcode');
|
||||||
|
const speakeasy = require('speakeasy');
|
||||||
|
const userModel = require('../models/user');
|
||||||
|
|
||||||
|
let router = express.Router({
|
||||||
|
caseSensitive: true,
|
||||||
|
strict: true,
|
||||||
|
mergeParams: true
|
||||||
|
});
|
||||||
|
|
||||||
|
router
|
||||||
|
.route('/')
|
||||||
|
.options((_, res) => {
|
||||||
|
res.sendStatus(204);
|
||||||
|
})
|
||||||
|
|
||||||
|
.get(async (req, res, next) => {
|
||||||
|
internalToken.getFreshToken(res.locals.access, {
|
||||||
|
expiry: (typeof req.query.expiry !== 'undefined' ? req.query.expiry : null),
|
||||||
|
scope: (typeof req.query.scope !== 'undefined' ? req.query.scope : null)
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
res.status(200)
|
||||||
|
.send(data);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
router
|
||||||
|
.route('/create')
|
||||||
|
.post(jwtdecode(), (req, res, next) => {
|
||||||
|
if (!res.locals.access) {
|
||||||
|
return next(new Error('Invalid token'));
|
||||||
|
}
|
||||||
|
const userId = res.locals.access.token.getUserId();
|
||||||
|
internalMfa.createMfaSecretForUser(userId)
|
||||||
|
.then((secret) => {
|
||||||
|
return userModel.query()
|
||||||
|
.where('id', '=', userId)
|
||||||
|
.first()
|
||||||
|
.then((user) => {
|
||||||
|
if (!user) {
|
||||||
|
return next(new Error('User not found'));
|
||||||
|
}
|
||||||
|
return { secret, user };
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(({ secret, user }) => {
|
||||||
|
const otpAuthUrl = speakeasy.otpauthURL({
|
||||||
|
secret: secret.ascii,
|
||||||
|
label: user.email,
|
||||||
|
issuer: 'Nginx Proxy Manager'
|
||||||
|
});
|
||||||
|
qrcode.toDataURL(otpAuthUrl, (err, dataUrl) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error generating QR code:', err);
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
res.status(200).send({ qrCode: dataUrl });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
router
|
||||||
|
.route('/enable')
|
||||||
|
.post(jwtdecode(), (req, res, next) => {
|
||||||
|
apiValidator(schema.getValidationSchema('/mfa', 'post'), req.body).then((params) => {
|
||||||
|
internalMfa.enableMfaForUser(res.locals.access.token.getUserId(), params.token)
|
||||||
|
.then(() => res.status(200).send({ success: true }))
|
||||||
|
.catch(next);
|
||||||
|
}
|
||||||
|
);});
|
||||||
|
|
||||||
|
router
|
||||||
|
.route('/check')
|
||||||
|
.get(jwtdecode(), (req, res, next) => {
|
||||||
|
internalMfa.isMfaEnabledForUser(res.locals.access.token.getUserId())
|
||||||
|
.then((active) => res.status(200).send({ active }))
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
41
backend/schema/paths/mfa/post.json
Normal file
41
backend/schema/paths/mfa/post.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"operationId": "enableMfa",
|
||||||
|
"summary": "Enable multi-factor authentication for a user",
|
||||||
|
"tags": ["MFA"],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "MFA Token Payload",
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"token": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["token"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "MFA enabled successfully",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"success": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,6 +22,10 @@
|
|||||||
"secret": {
|
"secret": {
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mfa_token": {
|
||||||
|
"minLength": 1,
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["identity", "secret"],
|
"required": ["identity", "secret"],
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": true,
|
||||||
"required": ["name", "nickname", "email"],
|
"required": ["name", "nickname", "email"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
|
@@ -15,6 +15,11 @@
|
|||||||
"$ref": "./paths/get.json"
|
"$ref": "./paths/get.json"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/mfa": {
|
||||||
|
"post": {
|
||||||
|
"$ref": "./paths/mfa/post.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
"/audit-log": {
|
"/audit-log": {
|
||||||
"get": {
|
"get": {
|
||||||
"$ref": "./paths/audit-log/get.json"
|
"$ref": "./paths/audit-log/get.json"
|
||||||
|
2160
backend/yarn.lock
2160
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -202,7 +202,46 @@ module.exports = {
|
|||||||
return fetch('get', '');
|
return fetch('get', '');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Mfa: {
|
||||||
|
create: function () {
|
||||||
|
return fetch('post', 'mfa/create');
|
||||||
|
},
|
||||||
|
enable: function (token) {
|
||||||
|
return fetch('post', 'mfa/enable', {token: token});
|
||||||
|
},
|
||||||
|
check: function () {
|
||||||
|
return fetch('get', 'mfa/check');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
Tokens: {
|
Tokens: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} identity
|
||||||
|
* @param {String} secret
|
||||||
|
* @param {String} token
|
||||||
|
* @param {Boolean} wipe
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
loginWithMFA: function (identity, secret, mfaToken, wipe) {
|
||||||
|
return fetch('post', 'tokens', {identity: identity, secret: secret, mfa_token: mfaToken})
|
||||||
|
.then(response => {
|
||||||
|
if (response.token) {
|
||||||
|
if (wipe) {
|
||||||
|
Tokens.clearTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set storage token
|
||||||
|
Tokens.addToken(response.token);
|
||||||
|
return response.token;
|
||||||
|
} else {
|
||||||
|
Tokens.clearTokens();
|
||||||
|
throw(new Error('No token returned'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} identity
|
* @param {String} identity
|
||||||
|
@@ -4,438 +4,444 @@ const Tokens = require('./tokens');
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} route
|
* @param {String} route
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
navigate: function (route, options) {
|
navigate: function (route, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
Backbone.history.navigate(route.toString(), options);
|
Backbone.history.navigate(route.toString(), options);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Login
|
* Login
|
||||||
*/
|
*/
|
||||||
showLogin: function () {
|
showLogin: function () {
|
||||||
window.location = '/login';
|
window.location = '/login';
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users
|
* Users
|
||||||
*/
|
*/
|
||||||
showUsers: function () {
|
showUsers: function () {
|
||||||
const controller = this;
|
let controller = this;
|
||||||
if (Cache.User.isAdmin()) {
|
if (Cache.User.isAdmin()) {
|
||||||
require(['./main', './users/main'], (App, View) => {
|
require(['./main', './users/main'], (App, View) => {
|
||||||
controller.navigate('/users');
|
controller.navigate('/users');
|
||||||
App.UI.showAppContent(new View());
|
App.UI.showAppContent(new View());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.showDashboard();
|
this.showDashboard();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Form
|
* User Form
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showUserForm: function (model) {
|
showUserForm: function (model) {
|
||||||
if (Cache.User.isAdmin()) {
|
if (Cache.User.isAdmin()) {
|
||||||
require(['./main', './user/form'], function (App, View) {
|
require(['./main', './user/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Permissions Form
|
* User Permissions Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showUserPermissions: function (model) {
|
showUserPermissions: function (model) {
|
||||||
if (Cache.User.isAdmin()) {
|
if (Cache.User.isAdmin()) {
|
||||||
require(['./main', './user/permissions'], function (App, View) {
|
require(['./main', './user/permissions'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Password Form
|
* User Password Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showUserPasswordForm: function (model) {
|
showUserPasswordForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
|
if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
|
||||||
require(['./main', './user/password'], function (App, View) {
|
require(['./main', './user/password'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Delete Confirm
|
* User Delete Confirm
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showUserDeleteConfirm: function (model) {
|
showUserDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
|
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
|
||||||
require(['./main', './user/delete'], function (App, View) {
|
require(['./main', './user/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dashboard
|
* Dashboard
|
||||||
*/
|
*/
|
||||||
showDashboard: function () {
|
showDashboard: function () {
|
||||||
const controller = this;
|
let controller = this;
|
||||||
require(['./main', './dashboard/main'], (App, View) => {
|
|
||||||
controller.navigate('/');
|
|
||||||
App.UI.showAppContent(new View());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
require(['./main', './dashboard/main'], (App, View) => {
|
||||||
* Nginx Proxy Hosts
|
controller.navigate('/');
|
||||||
*/
|
App.UI.showAppContent(new View());
|
||||||
showNginxProxy: function () {
|
});
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
},
|
||||||
const controller = this;
|
|
||||||
|
|
||||||
require(['./main', './nginx/proxy/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/nginx/proxy');
|
* Nginx Proxy Hosts
|
||||||
App.UI.showAppContent(new View());
|
*/
|
||||||
});
|
showNginxProxy: function () {
|
||||||
}
|
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
||||||
},
|
let controller = this;
|
||||||
|
|
||||||
/**
|
require(['./main', './nginx/proxy/main'], (App, View) => {
|
||||||
* Nginx Proxy Host Form
|
controller.navigate('/nginx/proxy');
|
||||||
*
|
App.UI.showAppContent(new View());
|
||||||
* @param [model]
|
});
|
||||||
*/
|
}
|
||||||
showNginxProxyForm: function (model) {
|
},
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
|
||||||
require(['./main', './nginx/proxy/form'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proxy Host Delete Confirm
|
* Nginx Proxy Host Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showNginxProxyDeleteConfirm: function (model) {
|
showNginxProxyForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||||
require(['./main', './nginx/proxy/delete'], function (App, View) {
|
require(['./main', './nginx/proxy/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Redirection Hosts
|
* Proxy Host Delete Confirm
|
||||||
*/
|
*
|
||||||
showNginxRedirection: function () {
|
* @param model
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
*/
|
||||||
const controller = this;
|
showNginxProxyDeleteConfirm: function (model) {
|
||||||
require(['./main', './nginx/redirection/main'], (App, View) => {
|
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||||
controller.navigate('/nginx/redirection');
|
require(['./main', './nginx/proxy/delete'], function (App, View) {
|
||||||
App.UI.showAppContent(new View());
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Redirection Host Form
|
* Nginx Redirection Hosts
|
||||||
*
|
*/
|
||||||
* @param [model]
|
showNginxRedirection: function () {
|
||||||
*/
|
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
||||||
showNginxRedirectionForm: function (model) {
|
let controller = this;
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
|
||||||
require(['./main', './nginx/redirection/form'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
require(['./main', './nginx/redirection/main'], (App, View) => {
|
||||||
* Proxy Redirection Delete Confirm
|
controller.navigate('/nginx/redirection');
|
||||||
*
|
App.UI.showAppContent(new View());
|
||||||
* @param model
|
});
|
||||||
*/
|
}
|
||||||
showNginxRedirectionDeleteConfirm: function (model) {
|
},
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
|
||||||
require(['./main', './nginx/redirection/delete'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Stream Hosts
|
* Nginx Redirection Host Form
|
||||||
*/
|
*
|
||||||
showNginxStream: function () {
|
* @param [model]
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
*/
|
||||||
const controller = this;
|
showNginxRedirectionForm: function (model) {
|
||||||
require(['./main', './nginx/stream/main'], (App, View) => {
|
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
||||||
controller.navigate('/nginx/stream');
|
require(['./main', './nginx/redirection/form'], function (App, View) {
|
||||||
App.UI.showAppContent(new View());
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream Form
|
* Proxy Redirection Delete Confirm
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxStreamForm: function (model) {
|
showNginxRedirectionDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
||||||
require(['./main', './nginx/stream/form'], function (App, View) {
|
require(['./main', './nginx/redirection/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream Delete Confirm
|
* Nginx Stream Hosts
|
||||||
*
|
*/
|
||||||
* @param model
|
showNginxStream: function () {
|
||||||
*/
|
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
||||||
showNginxStreamDeleteConfirm: function (model) {
|
let controller = this;
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
|
||||||
require(['./main', './nginx/stream/delete'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
require(['./main', './nginx/stream/main'], (App, View) => {
|
||||||
* Nginx Dead Hosts
|
controller.navigate('/nginx/stream');
|
||||||
*/
|
App.UI.showAppContent(new View());
|
||||||
showNginxDead: function () {
|
});
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
|
}
|
||||||
const controller = this;
|
},
|
||||||
require(['./main', './nginx/dead/main'], (App, View) => {
|
|
||||||
controller.navigate('/nginx/404');
|
|
||||||
App.UI.showAppContent(new View());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dead Host Form
|
* Stream Form
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showNginxDeadForm: function (model) {
|
showNginxStreamForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||||
require(['./main', './nginx/dead/form'], function (App, View) {
|
require(['./main', './nginx/stream/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dead Host Delete Confirm
|
* Stream Delete Confirm
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxDeadDeleteConfirm: function (model) {
|
showNginxStreamDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||||
require(['./main', './nginx/dead/delete'], function (App, View) {
|
require(['./main', './nginx/stream/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Help Dialog
|
* Nginx Dead Hosts
|
||||||
*
|
*/
|
||||||
* @param {String} title
|
showNginxDead: function () {
|
||||||
* @param {String} content
|
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
|
||||||
*/
|
let controller = this;
|
||||||
showHelp: function (title, content) {
|
|
||||||
require(['./main', './help/main'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({title: title, content: content}));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
require(['./main', './nginx/dead/main'], (App, View) => {
|
||||||
* Nginx Access
|
controller.navigate('/nginx/404');
|
||||||
*/
|
App.UI.showAppContent(new View());
|
||||||
showNginxAccess: function () {
|
});
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
|
}
|
||||||
const controller = this;
|
},
|
||||||
require(['./main', './nginx/access/main'], (App, View) => {
|
|
||||||
controller.navigate('/nginx/access');
|
|
||||||
App.UI.showAppContent(new View());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Access List Form
|
* Dead Host Form
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showNginxAccessListForm: function (model) {
|
showNginxDeadForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||||
require(['./main', './nginx/access/form'], function (App, View) {
|
require(['./main', './nginx/dead/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access List Delete Confirm
|
* Dead Host Delete Confirm
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxAccessListDeleteConfirm: function (model) {
|
showNginxDeadDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||||
require(['./main', './nginx/access/delete'], function (App, View) {
|
require(['./main', './nginx/dead/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Certificates
|
* Help Dialog
|
||||||
*/
|
*
|
||||||
showNginxCertificates: function () {
|
* @param {String} title
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
* @param {String} content
|
||||||
const controller = this;
|
*/
|
||||||
require(['./main', './nginx/certificates/main'], (App, View) => {
|
showHelp: function (title, content) {
|
||||||
controller.navigate('/nginx/certificates');
|
require(['./main', './help/main'], function (App, View) {
|
||||||
App.UI.showAppContent(new View());
|
App.UI.showModalDialog(new View({title: title, content: content}));
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Certificate Form
|
* Nginx Access
|
||||||
*
|
*/
|
||||||
* @param [model]
|
showNginxAccess: function () {
|
||||||
*/
|
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
|
||||||
showNginxCertificateForm: function (model) {
|
let controller = this;
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
|
||||||
require(['./main', './nginx/certificates/form'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
require(['./main', './nginx/access/main'], (App, View) => {
|
||||||
* Certificate Renew
|
controller.navigate('/nginx/access');
|
||||||
*
|
App.UI.showAppContent(new View());
|
||||||
* @param model
|
});
|
||||||
*/
|
}
|
||||||
showNginxCertificateRenew: function (model) {
|
},
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
|
||||||
require(['./main', './nginx/certificates/renew'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certificate Delete Confirm
|
* Nginx Access List Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showNginxCertificateDeleteConfirm: function (model) {
|
showNginxAccessListForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||||
require(['./main', './nginx/certificates/delete'], function (App, View) {
|
require(['./main', './nginx/access/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certificate Test Reachability
|
* Access List Delete Confirm
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxCertificateTestReachability: function (model) {
|
showNginxAccessListDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||||
require(['./main', './nginx/certificates/test'], function (App, View) {
|
require(['./main', './nginx/access/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audit Log
|
* Nginx Certificates
|
||||||
*/
|
*/
|
||||||
showAuditLog: function () {
|
showNginxCertificates: function () {
|
||||||
const controller = this;
|
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
||||||
if (Cache.User.isAdmin()) {
|
let controller = this;
|
||||||
require(['./main', './audit-log/main'], (App, View) => {
|
|
||||||
controller.navigate('/audit-log');
|
|
||||||
App.UI.showAppContent(new View());
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.showDashboard();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
require(['./main', './nginx/certificates/main'], (App, View) => {
|
||||||
* Audit Log Metadata
|
controller.navigate('/nginx/certificates');
|
||||||
*
|
App.UI.showAppContent(new View());
|
||||||
* @param model
|
});
|
||||||
*/
|
}
|
||||||
showAuditMeta: function (model) {
|
},
|
||||||
if (Cache.User.isAdmin()) {
|
|
||||||
require(['./main', './audit-log/meta'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings
|
* Nginx Certificate Form
|
||||||
*/
|
*
|
||||||
showSettings: function () {
|
* @param [model]
|
||||||
const controller = this;
|
*/
|
||||||
if (Cache.User.isAdmin()) {
|
showNginxCertificateForm: function (model) {
|
||||||
require(['./main', './settings/main'], (App, View) => {
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
controller.navigate('/settings');
|
require(['./main', './nginx/certificates/form'], function (App, View) {
|
||||||
App.UI.showAppContent(new View());
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
this.showDashboard();
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings Item Form
|
* Certificate Renew
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showSettingForm: function (model) {
|
showNginxCertificateRenew: function (model) {
|
||||||
if (Cache.User.isAdmin()) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
if (model.get('id') === 'default-site') {
|
require(['./main', './nginx/certificates/renew'], function (App, View) {
|
||||||
require(['./main', './settings/default-site/main'], function (App, View) {
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
});
|
||||||
});
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logout
|
* Certificate Delete Confirm
|
||||||
*/
|
*
|
||||||
logout: function () {
|
* @param model
|
||||||
Tokens.dropTopToken();
|
*/
|
||||||
this.showLogin();
|
showNginxCertificateDeleteConfirm: function (model) {
|
||||||
}
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
|
require(['./main', './nginx/certificates/delete'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certificate Test Reachability
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
showNginxCertificateTestReachability: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
|
require(['./main', './nginx/certificates/test'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Audit Log
|
||||||
|
*/
|
||||||
|
showAuditLog: function () {
|
||||||
|
let controller = this;
|
||||||
|
if (Cache.User.isAdmin()) {
|
||||||
|
require(['./main', './audit-log/main'], (App, View) => {
|
||||||
|
controller.navigate('/audit-log');
|
||||||
|
App.UI.showAppContent(new View());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.showDashboard();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Audit Log Metadata
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
showAuditMeta: function (model) {
|
||||||
|
if (Cache.User.isAdmin()) {
|
||||||
|
require(['./main', './audit-log/meta'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings
|
||||||
|
*/
|
||||||
|
showSettings: function () {
|
||||||
|
let controller = this;
|
||||||
|
if (Cache.User.isAdmin()) {
|
||||||
|
require(['./main', './settings/main'], (App, View) => {
|
||||||
|
controller.navigate('/settings');
|
||||||
|
App.UI.showAppContent(new View());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.showDashboard();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings Item Form
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
showSettingForm: function (model) {
|
||||||
|
if (Cache.User.isAdmin()) {
|
||||||
|
if (model.get('id') === 'default-site') {
|
||||||
|
require(['./main', './settings/default-site/main'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout
|
||||||
|
*/
|
||||||
|
logout: function () {
|
||||||
|
Tokens.dropTopToken();
|
||||||
|
this.showLogin();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@@ -6,85 +6,87 @@ const Helpers = require('../../lib/helpers');
|
|||||||
const template = require('./main.ejs');
|
const template = require('./main.ejs');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
template: template,
|
template: template,
|
||||||
id: 'dashboard',
|
id: 'dashboard',
|
||||||
columns: 0,
|
columns: 0,
|
||||||
|
|
||||||
stats: {},
|
stats: {},
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
links: 'a'
|
links: 'a'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click @ui.links': function (e) {
|
'click @ui.links': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.navigate($(e.currentTarget).attr('href'), true);
|
Controller.navigate($(e.currentTarget).attr('href'), true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
templateContext: function () {
|
templateContext: function () {
|
||||||
const view = this;
|
let view = this;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getUserName: function () {
|
getUserName: function () {
|
||||||
return Cache.User.get('nickname') || Cache.User.get('name');
|
return Cache.User.get('nickname') || Cache.User.get('name');
|
||||||
},
|
},
|
||||||
|
|
||||||
getHostStat: function (type) {
|
getHostStat: function (type) {
|
||||||
if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
|
if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
|
||||||
return Helpers.niceNumber(view.stats.hosts[type]);
|
return Helpers.niceNumber(view.stats.hosts[type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return '-';
|
return '-';
|
||||||
},
|
},
|
||||||
|
|
||||||
canShow: function (perm) {
|
canShow: function (perm) {
|
||||||
return Cache.User.isAdmin() || Cache.User.canView(perm);
|
return Cache.User.isAdmin() || Cache.User.canView(perm);
|
||||||
},
|
},
|
||||||
|
|
||||||
columns: view.columns
|
columns: view.columns
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
const view = this;
|
let view = this;
|
||||||
if (typeof view.stats.hosts === 'undefined') {
|
|
||||||
Api.Reports.getHostStats()
|
|
||||||
.then(response => {
|
|
||||||
if (!view.isDestroyed()) {
|
|
||||||
view.stats.hosts = response;
|
|
||||||
view.render();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
if (typeof view.stats.hosts === 'undefined') {
|
||||||
* @param {Object} [model]
|
Api.Reports.getHostStats()
|
||||||
*/
|
.then(response => {
|
||||||
preRender: function (model) {
|
if (!view.isDestroyed()) {
|
||||||
this.columns = 0;
|
view.stats.hosts = response;
|
||||||
|
view.render();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// calculate the available columns based on permissions for the objects
|
/**
|
||||||
// and store as a variable
|
* @param {Object} [model]
|
||||||
const perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
*/
|
||||||
|
preRender: function (model) {
|
||||||
|
this.columns = 0;
|
||||||
|
|
||||||
perms.map(perm => {
|
// calculate the available columns based on permissions for the objects
|
||||||
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
// and store as a variable
|
||||||
});
|
//let view = this;
|
||||||
|
let perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
||||||
|
|
||||||
// Prevent double rendering on initial calls
|
perms.map(perm => {
|
||||||
if (typeof model !== 'undefined') {
|
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
||||||
this.render();
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize: function () {
|
// Prevent double rendering on initial calls
|
||||||
this.preRender();
|
if (typeof model !== 'undefined') {
|
||||||
this.listenTo(Cache.User, 'change', this.preRender);
|
this.render();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.preRender();
|
||||||
|
this.listenTo(Cache.User, 'change', this.preRender);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@@ -25,6 +25,17 @@
|
|||||||
<div class="invalid-feedback secret-error"></div>
|
<div class="invalid-feedback secret-error"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<label class="form-label mfa-label" style="display: none;"><%- i18n('mfa', 'mfa') %></label>
|
||||||
|
<button type="button" class="btn btn-info add-mfa"><%- i18n('mfa', 'add-mfa') %></button>
|
||||||
|
<p class="qr-instructions" style="display: none;"><%- i18n('mfa', 'mfa-setup-instruction') %></p>
|
||||||
|
<div class="mfa-validation-container" style="display: none;">
|
||||||
|
<label class="form-label"><%- i18n('mfa', 'mfa-token') %> <span class="form-required">*</span></label>
|
||||||
|
<input name="mfa_validation" type="text" class="form-control" placeholder="000000" value="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<% if (isAdmin() && !isSelf()) { %>
|
<% if (isAdmin() && !isSelf()) { %>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-label"><%- i18n('roles', 'title') %></div>
|
<div class="form-label"><%- i18n('roles', 'title') %></div>
|
||||||
|
@@ -14,7 +14,11 @@ module.exports = Mn.View.extend({
|
|||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
save: 'button.save',
|
save: 'button.save',
|
||||||
error: '.secret-error'
|
error: '.secret-error',
|
||||||
|
addMfa: '.add-mfa',
|
||||||
|
mfaLabel: '.mfa-label', // added binding
|
||||||
|
mfaValidation: '.mfa-validation-container', // added binding
|
||||||
|
qrInstructions: '.qr-instructions' // added binding for instructions
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
@@ -25,6 +29,10 @@ module.exports = Mn.View.extend({
|
|||||||
let view = this;
|
let view = this;
|
||||||
let data = this.ui.form.serializeJSON();
|
let data = this.ui.form.serializeJSON();
|
||||||
|
|
||||||
|
// Save "mfa_validation" value and remove it from data
|
||||||
|
let mfaToken = data.mfa_validation;
|
||||||
|
delete data.mfa_validation;
|
||||||
|
|
||||||
let show_password = this.model.get('email') === 'admin@example.com';
|
let show_password = this.model.get('email') === 'admin@example.com';
|
||||||
|
|
||||||
// admin@example.com is not allowed
|
// admin@example.com is not allowed
|
||||||
@@ -62,6 +70,15 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
view.model.set(result);
|
view.model.set(result);
|
||||||
|
|
||||||
|
if (mfaToken) {
|
||||||
|
return App.Api.Mfa.enable(mfaToken)
|
||||||
|
.then(() => result);
|
||||||
|
}
|
||||||
|
console.log(result);
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
App.UI.closeModal(function () {
|
App.UI.closeModal(function () {
|
||||||
if (method === App.Api.Users.create) {
|
if (method === App.Api.Users.create) {
|
||||||
// Show permissions dialog immediately
|
// Show permissions dialog immediately
|
||||||
@@ -75,6 +92,20 @@ module.exports = Mn.View.extend({
|
|||||||
this.ui.error.text(err.message).show();
|
this.ui.error.text(err.message).show();
|
||||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
'click @ui.addMfa': function (e) {
|
||||||
|
let view = this;
|
||||||
|
App.Api.Mfa.create()
|
||||||
|
.then(response => {
|
||||||
|
view.ui.addMfa.replaceWith(`<img class="qr-code" src="${response.qrCode}" alt="QR Code">`);
|
||||||
|
view.ui.qrInstructions.show();
|
||||||
|
view.ui.mfaValidation.show();
|
||||||
|
// Add required attribute once MFA is activated
|
||||||
|
view.ui.mfaValidation.find('input[name="mfa_validation"]').attr('required', true);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
view.ui.error.text(err.message).show();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -104,5 +135,29 @@ module.exports = Mn.View.extend({
|
|||||||
if (typeof options.model === 'undefined' || !options.model) {
|
if (typeof options.model === 'undefined' || !options.model) {
|
||||||
this.model = new UserModel.Model();
|
this.model = new UserModel.Model();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender: function () {
|
||||||
|
let view = this;
|
||||||
|
App.Api.Mfa.check()
|
||||||
|
.then(response => {
|
||||||
|
if (response.active) {
|
||||||
|
view.ui.addMfa.hide();
|
||||||
|
view.ui.mfaLabel.hide();
|
||||||
|
view.ui.qrInstructions.hide();
|
||||||
|
view.ui.mfaValidation.hide();
|
||||||
|
// Remove required attribute if MFA is active & field is hidden
|
||||||
|
view.ui.mfaValidation.find('input[name="mfa_validation"]').removeAttr('required');
|
||||||
|
} else {
|
||||||
|
view.ui.addMfa.show();
|
||||||
|
view.ui.mfaLabel.show();
|
||||||
|
view.ui.qrInstructions.hide();
|
||||||
|
view.ui.mfaValidation.hide();
|
||||||
|
view.ui.mfaValidation.find('input[name="mfa_validation"]').removeAttr('required');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
view.ui.error.text(err.message).show();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -37,8 +37,15 @@
|
|||||||
"all": "All",
|
"all": "All",
|
||||||
"any": "Any"
|
"any": "Any"
|
||||||
},
|
},
|
||||||
|
"mfa": {
|
||||||
|
"mfa": "Multi Factor Authentication",
|
||||||
|
"add-mfa": "Generate secret",
|
||||||
|
"mfa-setup-instruction": "Scan this QR code in your authenticator app to set up MFA and then enter the current MFA code in the input field.",
|
||||||
|
"mfa-token": "Multi factor authentication token"
|
||||||
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"title": "Login to your account"
|
"title": "Login to your account",
|
||||||
|
"mfa-required-text": "Please enter your MFA token to continue"
|
||||||
},
|
},
|
||||||
"main": {
|
"main": {
|
||||||
"app": "Nginx Proxy Manager",
|
"app": "Nginx Proxy Manager",
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-login mx-auto">
|
<div class="col col-login mx-auto">
|
||||||
@@ -24,6 +25,12 @@
|
|||||||
<input name="secret" type="password" class="form-control" placeholder="<%- i18n('str', 'password') %>" required>
|
<input name="secret" type="password" class="form-control" placeholder="<%- i18n('str', 'password') %>" required>
|
||||||
<div class="invalid-feedback secret-error"></div>
|
<div class="invalid-feedback secret-error"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group mfa-group" style="display: none;">
|
||||||
|
<p class="mfa-info"><%- i18n('login', 'mfa-required-text') %>:</p>
|
||||||
|
<label class="form-label"><%- i18n('mfa', 'mfa-token') %></label>
|
||||||
|
<input name="mfa_token" type="text" class="form-control" placeholder="<%- i18n('mfa', 'mfa-token') %>">
|
||||||
|
<div class="invalid-feedback mfa-error"></div>
|
||||||
|
</div>
|
||||||
<div class="form-footer">
|
<div class="form-footer">
|
||||||
<button type="submit" class="btn btn-teal btn-block"><%- i18n('str', 'sign-in') %></button>
|
<button type="submit" class="btn btn-teal btn-block"><%- i18n('str', 'sign-in') %></button>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,4 +41,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@@ -13,7 +13,11 @@ module.exports = Mn.View.extend({
|
|||||||
identity: 'input[name="identity"]',
|
identity: 'input[name="identity"]',
|
||||||
secret: 'input[name="secret"]',
|
secret: 'input[name="secret"]',
|
||||||
error: '.secret-error',
|
error: '.secret-error',
|
||||||
button: 'button'
|
error_mfa:'.mfa-error',
|
||||||
|
button: 'button',
|
||||||
|
mfaGroup: '.mfa-group', // added MFA group selector
|
||||||
|
mfaToken: 'input[name="mfa_token"]', // added MFA token input
|
||||||
|
mfaInfo: '.mfa-info' // added MFA info element
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
@@ -22,14 +26,36 @@ module.exports = Mn.View.extend({
|
|||||||
this.ui.button.addClass('btn-loading').prop('disabled', true);
|
this.ui.button.addClass('btn-loading').prop('disabled', true);
|
||||||
this.ui.error.hide();
|
this.ui.error.hide();
|
||||||
|
|
||||||
Api.Tokens.login(this.ui.identity.val(), this.ui.secret.val(), true)
|
if(this.ui.mfaToken.val()) {
|
||||||
|
Api.Tokens.loginWithMFA(this.ui.identity.val(), this.ui.secret.val(), this.ui.mfaToken.val(), true)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.location = '/';
|
window.location = '/';
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.ui.error.text(err.message).show();
|
if (err.message === 'Invalid MFA token.') {
|
||||||
|
this.ui.error_mfa.text(err.message).show();
|
||||||
|
} else {
|
||||||
|
this.ui.error.text(err.message).show();
|
||||||
|
}
|
||||||
this.ui.button.removeClass('btn-loading').prop('disabled', false);
|
this.ui.button.removeClass('btn-loading').prop('disabled', false);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Api.Tokens.login(this.ui.identity.val(), this.ui.secret.val(), true)
|
||||||
|
.then(() => {
|
||||||
|
window.location = '/';
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (err.message === 'MFA token required') {
|
||||||
|
this.ui.mfaGroup.show();
|
||||||
|
this.ui.mfaInfo.show();
|
||||||
|
} else {
|
||||||
|
this.ui.error.text(err.message).show();
|
||||||
|
}
|
||||||
|
this.ui.button.removeClass('btn-loading').prop('disabled', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -40,3 +66,5 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@@ -161,11 +161,11 @@
|
|||||||
},
|
},
|
||||||
"domainoffensive": {
|
"domainoffensive": {
|
||||||
"name": "DomainOffensive (do.de)",
|
"name": "DomainOffensive (do.de)",
|
||||||
"package_name": "certbot-dns-domainoffensive",
|
"package_name": "certbot-dns-do",
|
||||||
"version": "~=2.0.0",
|
"version": "~=0.31.0",
|
||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"credentials": "dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN",
|
"credentials": "dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN",
|
||||||
"full_plugin_name": "dns-domainoffensive"
|
"full_plugin_name": "dns-do"
|
||||||
},
|
},
|
||||||
"domeneshop": {
|
"domeneshop": {
|
||||||
"name": "Domeneshop",
|
"name": "Domeneshop",
|
||||||
@@ -534,13 +534,5 @@
|
|||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"credentials": "edgedns_client_secret = as3d1asd5d1a32sdfsdfs2d1asd5=\nedgedns_host = sdflskjdf-dfsdfsdf-sdfsdfsdf.luna.akamaiapis.net\nedgedns_access_token = kjdsi3-34rfsdfsdf-234234fsdfsdf\nedgedns_client_token = dkfjdf-342fsdfsd-23fsdfsdfsdf",
|
"credentials": "edgedns_client_secret = as3d1asd5d1a32sdfsdfs2d1asd5=\nedgedns_host = sdflskjdf-dfsdfsdf-sdfsdfsdf.luna.akamaiapis.net\nedgedns_access_token = kjdsi3-34rfsdfsdf-234234fsdfsdf\nedgedns_client_token = dkfjdf-342fsdfsd-23fsdfsdfsdf",
|
||||||
"full_plugin_name": "edgedns"
|
"full_plugin_name": "edgedns"
|
||||||
},
|
}
|
||||||
"zoneedit": {
|
|
||||||
"name": "ZoneEdit",
|
|
||||||
"package_name": "certbot-dns-zoneedit",
|
|
||||||
"version": "~=0.3.2",
|
|
||||||
"dependencies": "--no-deps dnspython",
|
|
||||||
"credentials": "dns_zoneedit_user = <login-user-id>\ndns_zoneedit_token = <dyn-authentication-token>",
|
|
||||||
"full_plugin_name": "dns-zoneedit"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user