From d2ae9c43e87296363648ffc3ee2278b52451109d Mon Sep 17 00:00:00 2001 From: garrettmills Date: Sat, 30 May 2020 20:16:10 -0500 Subject: [PATCH] Finish server-side translations in controllers --- app/controllers/api/v1/App.controller.js | 38 ++++---- app/controllers/api/v1/Auth.controller.js | 94 +++++++++---------- app/controllers/api/v1/IAM.controller.js | 52 +++++----- app/controllers/api/v1/LDAP.controller.js | 54 +++++------ app/controllers/api/v1/Message.controller.js | 4 +- app/controllers/api/v1/OAuth.controller.js | 26 ++--- app/controllers/api/v1/Password.controller.js | 16 ++-- app/controllers/api/v1/Profile.controller.js | 12 +-- app/controllers/api/v1/Reflect.controller.js | 20 ++-- app/controllers/api/v1/SAML.controller.js | 8 +- app/controllers/api/v1/Settings.controller.js | 4 +- app/controllers/auth/Forms.controller.js | 4 +- app/controllers/auth/MFA.controller.js | 10 +- app/controllers/auth/Oauth2.controller.js | 12 +-- app/controllers/auth/Trust.controller.js | 15 +-- app/controllers/dash/Groups.controller.js | 35 ------- app/controllers/dash/SAML.controller.js | 78 --------------- app/controllers/dash/Users.controller.js | 47 ---------- app/controllers/saml/SAML.controller.js | 10 +- app/routing/routers/dash/groups.routes.js | 13 --- app/routing/routers/dash/saml.routes.js | 13 --- app/routing/routers/dash/users.routes.js | 13 --- locale/en_US/api.locale.js | 17 ++++ locale/en_US/auth.locale.js | 15 +++ locale/en_US/common.locale.js | 4 + locale/en_US/saml.locale.js | 5 + 26 files changed, 225 insertions(+), 394 deletions(-) delete mode 100644 app/controllers/dash/Groups.controller.js delete mode 100644 app/controllers/dash/SAML.controller.js delete mode 100644 app/controllers/dash/Users.controller.js delete mode 100644 app/routing/routers/dash/groups.routes.js delete mode 100644 app/routing/routers/dash/saml.routes.js delete mode 100644 app/routing/routers/dash/users.routes.js create mode 100644 locale/en_US/saml.locale.js diff --git a/app/controllers/api/v1/App.controller.js b/app/controllers/api/v1/App.controller.js index 145b417..7a30862 100644 --- a/app/controllers/api/v1/App.controller.js +++ b/app/controllers/api/v1/App.controller.js @@ -25,12 +25,12 @@ class AppController extends Controller { if ( !application || !application.active ) return res.status(404) - .message(req.T('api:application_not_found')) + .message(req.T('api.application_not_found')) .api() if ( !req.user.can(`app:${application.id}:view`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() return res.api(await application.to_api()) @@ -41,28 +41,28 @@ class AppController extends Controller { if ( !req.user.can('app:create') ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['name', 'identifier'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`${req.T('api:missing_field')} ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } // Make sure the identifier is properly formatted if ( !(new RegExp('^[a-zA-Z0-9_]*$')).test(req.body.identifier) ) return res.status(400) - .message(`${req.T('api:improper_field')} identifier ${req.T('api:alphanum_underscores')}`) + .message(`${req.T('api.improper_field')} identifier ${req.T('api.alphanum_underscores')}`) .api() // Make sure the identifier is unique const existing_app = await Application.findOne({ identifier: req.body.identifier }) if ( existing_app ) return res.status(400) - .message(req.T('api:application_already_exists')) + .message(req.T('api.application_already_exists')) .api() const application = new Application({ @@ -80,7 +80,7 @@ class AppController extends Controller { const client = await LDAPClient.findById(id) if ( !client || !client.active || !req.user.can(`ldap:client:${client.id}:view`) ) return res.status(400) - .message(`${req.T('api:invalid_ldap_client_id')} ${id}`) + .message(`${req.T('api.invalid_ldap_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ ldap_client_ids: client.id }) @@ -102,7 +102,7 @@ class AppController extends Controller { const client = await OAuthClient.findById(id) if ( !client || !client.active || !req.user.can(`oauth:client:${client.id}:view`) ) return res.status(400) - .message(`${req.T('api:invalid_oauth_client_id')} ${id}`) + .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ oauth_client_ids: client.id }) @@ -124,7 +124,7 @@ class AppController extends Controller { const provider = await ServiceProvider.findById(id) if ( !provider || !provider.active || !req.user.can(`saml:provider:${provider.id}:view`) ) return res.status(400) - .message(`${req.T('api:invalid_saml_service_provider_id')} ${id}`) + .message(`${req.T('api.invalid_saml_service_provider_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ saml_service_provider_ids: provider.id }) @@ -147,33 +147,33 @@ class AppController extends Controller { if ( !application || !application.active ) return res.status(404) - .message(req.T('api:application_not_found')) + .message(req.T('api.application_not_found')) .api() if ( !req.user.can(`app:${application.id}:update`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['name', 'identifier'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`${req.T('api:missing_field')} ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } // Make sure the identifier is properly formatted if ( !(new RegExp('^[a-zA-Z0-9_]*$')).test(req.body.identifier) ) return res.status(400) - .message(`${req.T('api:improper_field')} identifier ${req.T('api:alphanum_underscores')}`) + .message(`${req.T('api.improper_field')} identifier ${req.T('api.alphanum_underscores')}`) .api() // Make sure the identifier is unique const existing_app = await Application.findOne({ identifier: req.body.identifier }) if ( existing_app && existing_app.id !== application.id ) return res.status(400) - .message(req.T('api:application_already_exists')) + .message(req.T('api.application_already_exists')) .api() // Verify LDAP client IDs @@ -185,7 +185,7 @@ class AppController extends Controller { const client = await LDAPClient.findById(id) if ( !client || !client.active || !req.user.can(`ldap:client:${client.id}:view`) ) return res.status(400) - .message(`${req.T('api:invalid_ldap_client_id')} ${id}`) + .message(`${req.T('api.invalid_ldap_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ ldap_client_ids: client.id }) @@ -207,7 +207,7 @@ class AppController extends Controller { const client = await OAuthClient.findById(id) if ( !client || !client.active || !req.user.can(`oauth:client:${client.id}:view`) ) return res.status(400) - .message(`${req.T('api:invalid_oauth_client_id')} ${id}`) + .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ oauth_client_ids: client.id }) @@ -229,7 +229,7 @@ class AppController extends Controller { const provider = await ServiceProvider.findById(id) if ( !provider || !provider.active || !req.user.can(`saml:provider:${provider.id}:view`) ) return res.status(400) - .message(`${req.T('api:invalid_saml_service_provider_id')} ${id}`) + .message(`${req.T('api.invalid_saml_service_provider_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ saml_service_provider_ids: provider.id }) @@ -255,12 +255,12 @@ class AppController extends Controller { if ( !application || !application.active ) return res.status(404) - .message(req.T('api:application_not_found')) + .message(req.T('api.application_not_found')) .api() if ( !req.user.can(`app:${application.id}:delete`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() application.active = false diff --git a/app/controllers/api/v1/Auth.controller.js b/app/controllers/api/v1/Auth.controller.js index a2fa8da..c31eead 100644 --- a/app/controllers/api/v1/Auth.controller.js +++ b/app/controllers/api/v1/Auth.controller.js @@ -20,7 +20,7 @@ class AuthController extends Controller { async get_traps(req, res, next) { const trap_config = this.configs.get('traps') - const data = [{ name: req.T('auth:none'), trap: '', redirect_to: '/' }] + const data = [{ name: req.T('auth.none'), trap: '', redirect_to: '/' }] for ( const name in trap_config.types ) { if ( !trap_config.types.hasOwnProperty(name) ) continue data.push({ @@ -44,18 +44,18 @@ class AuthController extends Controller { for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`${req.T('api:missing_field')} ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } if ( !req.body.uid.match(/^([A-Z]|[a-z]|[0-9]|_|-|\.)+$/) ) return res.status(400) - .message(`${req.T('api:improper_field')} uid ${req.T('api:alphanum_underscores')}`) + .message(`${req.T('api.improper_field')} uid ${req.T('api.alphanum_underscores')}`) .api() if ( !email_validator.validate(req.body.email) ) return res.status(400) - .message(`${req.T('api:improper_field')} email`) + .message(`${req.T('api.improper_field')} email`) .api() for ( const field of unique_fields ) { @@ -64,7 +64,7 @@ class AuthController extends Controller { const match_user = await User.findOne(params) if ( match_user ) return res.status(400) - .message(`${req.T('auth:user_exists_with_field')} ${field}`) + .message(`${req.T('auth.user_exists_with_field')} ${field}`) .api() } @@ -91,12 +91,12 @@ class AuthController extends Controller { || req.user.mfa_token.recovery_codes.length < 1 ) return res.status(400) - .message(req.T('auth:no_mfa_or_recovery')) + .message(req.T('auth.no_mfa_or_recovery')) .api() if ( !req.body.code ) return res.status(400) - .message(`${req.T('api:missing_field')} code`) + .message(`${req.T('api.missing_field')} code`) .api() const success = await req.user.mfa_token.attempt_recovery(req.body.code) @@ -176,7 +176,7 @@ class AuthController extends Controller { if ( !group || !group.active ) return res.status(404) - .message(req.T('api:group_not_found')) + .message(req.T('api.group_not_found')) .api() if ( !req.user.can(`auth:group:${group.id}:view`) ) @@ -196,12 +196,12 @@ class AuthController extends Controller { if ( !user ) return res.status(404) - .message(req.T('api:user_not_found')) + .message(req.T('api.user_not_found')) .api() if ( !req.user.can(`auth:user:${user.id}:view`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() return res.api(await user.to_api()) @@ -210,12 +210,12 @@ class AuthController extends Controller { async create_group(req, res, next) { if ( !req.user.can(`auth:group:create`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() if ( !req.body.name ) return res.status(400) - .message(`${req.T('api:missing_field')} name`) + .message(`${req.T('api.missing_field')} name`) .api() const Group = this.models.get('auth:Group') @@ -224,7 +224,7 @@ class AuthController extends Controller { const existing_group = await Group.findOne({ name: req.body.name }) if ( existing_group ) return res.status(400) - .message(req.T('api:group_already_exists')) + .message(req.T('api.group_already_exists')) .api() const group = new Group({ name: req.body.name }) @@ -238,7 +238,7 @@ class AuthController extends Controller { const user = await User.findById(user_id) if ( !user ) return res.status(400) - .message(`${req.T('common:invalid')} user_id.`) + .message(`${req.T('common.invalid')} user_id.`) .api() } @@ -252,14 +252,14 @@ class AuthController extends Controller { async create_user(req, res, next) { if ( !req.user.can('auth:user:create') ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['uid', 'first_name', 'last_name', 'email', 'password'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`${req.T('api:missing_field')} ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } @@ -272,7 +272,7 @@ class AuthController extends Controller { const existing_user = await User.findOne(filter) if ( existing_user ) return res.status(400) - .message(`${req.T('auth:user_exists_with_field')} ${field}`) + .message(`${req.T('auth.user_exists_with_field')} ${field}`) .api() } @@ -281,7 +281,7 @@ class AuthController extends Controller { const result = zxcvbn(req.body.password) if ( result.score < min_score ) return res.status(400) - .message(req.T('auth:password_complexity_fail').replace('MIN_SCORE', min_score)) + .message(req.T('auth.password_complexity_fail').replace('MIN_SCORE', min_score)) .api() const user = new User({ @@ -297,7 +297,7 @@ class AuthController extends Controller { if ( req.body.trap ) { if ( !req.trap.trap_exists(req.body.trap) ) return res.status(400) - .message(req.T('auth:invalid_trap')) + .message(req.T('auth.invalid_trap')) .api() user.trap = req.body.trap @@ -315,24 +315,24 @@ class AuthController extends Controller { const group = await Group.findById(req.params.id) if ( !group ) return res.status(404) - .message(req.T('api:group_not_found')) + .message(req.T('api.group_not_found')) .api() if ( !req.user.can(`auth:group:${group.id}:update`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() if ( !req.body.name ) return res.status(400) - .message(`${req.T('api:missing_field')} name`) + .message(`${req.T('api.missing_field')} name`) .api() // Make sure the group name is unique const existing_group = await Group.findOne({ name: req.body.name }) if ( existing_group && existing_group.id !== group.id ) return res.status(400) - .message(req.T('api:group_already_exists')) + .message(req.T('api.group_already_exists')) .api() // Validate user_ids @@ -343,7 +343,7 @@ class AuthController extends Controller { const user = await User.findById(user_id) if ( !user ) return res.status(400) - .message(`${req.T('common:invalid')} user_id.`) + .message(`${req.T('common.invalid')} user_id.`) .api() } @@ -363,19 +363,19 @@ class AuthController extends Controller { if ( !user ) return res.status(404) - .message(req.T('api:user_not_found')) + .message(req.T('api.user_not_found')) .api() if ( !req.user.can(`auth:user:${user.id}:update`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['uid', 'first_name', 'last_name', 'email'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`${req.T('api:missing_field')} ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } @@ -387,7 +387,7 @@ class AuthController extends Controller { const existing_user = await User.findOne(filter) if ( existing_user && existing_user.id !== user.id ) return res.status(400) - .message(`${req.T('auth:user_exists_with_field')} ${field}`) + .message(`${req.T('auth.user_exists_with_field')} ${field}`) .api() } @@ -397,7 +397,7 @@ class AuthController extends Controller { const result = zxcvbn(req.body.password) if (result.score < min_score) return res.status(400) - .message(req.T('auth:password_complexity_fail').replace('MIN_SCORE', min_score)) + .message(req.T('auth.password_complexity_fail').replace('MIN_SCORE', min_score)) .api() await user.reset_password(req.body.password, 'api') @@ -416,7 +416,7 @@ class AuthController extends Controller { if ( req.body.trap ) { if ( !req.trap.trap_exists(req.body.trap) ) return res.status(400) - .message(req.T('auth:invalid_trap')) + .message(req.T('auth.invalid_trap')) .api() user.trap = req.body.trap @@ -433,12 +433,12 @@ class AuthController extends Controller { if ( !group ) return res.status(404) - .message(req.T('api:group_not_found')) + .message(req.T('api.group_not_found')) .api() if ( !req.user.can(`auth:group:${group.id}:delete`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() group.active = false @@ -452,12 +452,12 @@ class AuthController extends Controller { if ( !user ) return res.status(404) - .message(req.T('api:user_not_found')) + .message(req.T('api.user_not_found')) .api() if ( !req.user.can(`auth:user:${user.id}:delete`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() // check if the user is an LDAP client. if so, delete the client @@ -493,7 +493,7 @@ class AuthController extends Controller { if ( !req.body.username && !req.body.email ) return res.status(400) - .message(`${req.T('api:provide_one')} username, email`) + .message(`${req.T('api.provide_one')} username, email`) .api() const data = {} @@ -529,7 +529,7 @@ class AuthController extends Controller { const errors = await flitter.validate_login(req.body) if ( errors && errors.length > 0 ) return res.status(400) - .message(req.T('auth:unable_to_complete')) + .message(req.T('auth.unable_to_complete')) .api({ errors }) const login_args = await flitter.get_login_args(req.body) @@ -537,9 +537,9 @@ class AuthController extends Controller { if ( !user ) return res.status(200) - .message(req.T('auth:invalid_un_or_pw')) + .message(req.T('auth.invalid_un_or_pw')) .api({ - message: req.T('auth:invalid_un_or_pw'), + message: req.T('auth.invalid_un_or_pw'), success: false, }) @@ -549,9 +549,9 @@ class AuthController extends Controller { const client = await Client.findOne({ user_id: user.id }) if ( client ) return res.status(200) - .message(req.T('auth:invalid_un_or_pw')) + .message(req.T('auth.invalid_un_or_pw')) .api({ - message: req.T('auth:invalid_un_or_pw'), + message: req.T('auth.invalid_un_or_pw'), success: false, }) @@ -585,7 +585,7 @@ class AuthController extends Controller { } } else { return res.status(401) - .message(req.T('auth:unable_to_grant_trust')) + .message(req.T('auth.unable_to_grant_trust')) .api() } } @@ -600,7 +600,7 @@ class AuthController extends Controller { async get_mfa_recovery(req, res, next) { if ( !req.user.mfa_enabled ) return res.status(400) - .message(req.T('auth:no_mfa')) + .message(req.T('auth.no_mfa')) .api() const token = req.user.mfa_token @@ -617,7 +617,7 @@ class AuthController extends Controller { async generate_mfa_recovery(req, res, next) { if ( !req.user.mfa_enabled ) return res.status(400) - .message(req.T('auth:no_mfa')) + .message(req.T('auth.no_mfa')) .api() const token = req.user.mfa_token @@ -631,7 +631,7 @@ class AuthController extends Controller { async generate_mfa_key(req, res, next) { if ( req.user.mfa_enabled ) return res.status(400) - .message(req.T('auth:already_has_mfa')) + .message(req.T('auth.already_has_mfa')) .api() const MFAToken = this.models.get('auth:MFAToken') @@ -654,7 +654,7 @@ class AuthController extends Controller { async attempt_mfa(req, res, next) { if ( !req.user.mfa_token ) return res.status(400) - .message(req.T('auth:no_mfa')) + .message(req.T('auth.no_mfa')) .api() const code = req.body.verify_code @@ -682,7 +682,7 @@ class AuthController extends Controller { async enable_mfa(req, res, next) { if ( !req.user.mfa_token ) return res.status(400) - .message(req.T('auth:no_mfa')) + .message(req.T('auth.no_mfa')) .api() req.user.mfa_enabled = true @@ -700,7 +700,7 @@ class AuthController extends Controller { async disable_mfa(req, res, next) { if ( !req.user.mfa_enabled ) return res.status(400) - .message(req.T('auth:no_mfa')) + .message(req.T('auth.no_mfa')) .api() req.user.mfa_enabled = false diff --git a/app/controllers/api/v1/IAM.controller.js b/app/controllers/api/v1/IAM.controller.js index 53e4fad..ced6e9b 100644 --- a/app/controllers/api/v1/IAM.controller.js +++ b/app/controllers/api/v1/IAM.controller.js @@ -10,7 +10,7 @@ class IAMController extends Controller { if ( !req.body.entity_id && !req.body.target_id ) return res.status(400) - .message(`${req.T('api:missing_field', true)} entity_id, target_id`) + .message(`${req.T('api.missing_field', true)} entity_id, target_id`) .api() return res.api(await Policy.check_entity_access(req.body.entity_id, req.body.target_id)) @@ -22,7 +22,7 @@ class IAMController extends Controller { if ( !req.body.target_id ) return res.status(400) - .message(`${req.T('api:missing_field')} target_id`) + .message(`${req.T('api.missing_field')} target_id`) .api() let user = req.user @@ -31,12 +31,12 @@ class IAMController extends Controller { if ( !user ) return res.status(404) - .message(req.T('api:user_not_found')) + .message(req.T('api.user_not_found')) .api() if ( !req.user.can(`auth:user:${user.id}:view`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() return res.api(await Policy.check_user_access(user, req.body.target_id)) @@ -62,12 +62,12 @@ class IAMController extends Controller { if ( !policy ) return res.status(404) - .message(req.T('iam:policy_not_found')) + .message(req.T('iam.policy_not_found')) .api() if ( !req.user.can(`iam:policy:${policy.id}:view`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() return res.api(await policy.to_api()) @@ -80,13 +80,13 @@ class IAMController extends Controller { for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`${req.T('api:missing_field')} ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } if ( !['user', 'group'].includes(req.body.entity_type) ) return res.status(400) - .message(`${req.T('iam:invalid_entity')} user, group`) + .message(`${req.T('iam.invalid_entity')} user, group`) .api() // Make sure the entity_id is valid @@ -95,25 +95,25 @@ class IAMController extends Controller { const user = await User.findById(req.body.entity_id) if ( !user || !req.user.can(`auth:user:${user.id}:view`) ) return res.status(400) - .message(`${req.T('common:invalid')} entity_id.`) + .message(`${req.T('common.invalid')} entity_id.`) .api() } else if ( req.body.entity_type === 'group' ) { const Group = this.models.get('auth:Group') const group = await Group.findById(req.body.entity_id) if ( !group || !group.active || !req.user.can(`auth:group:${group.id}:view`) ) return res.status(400) - .message(`${req.T('common:invalid')} entity_id.`) + .message(`${req.T('common.invalid')} entity_id.`) .api() } if ( !['allow', 'deny'].includes(req.body.access_type) ) return res.status(400) - .message(`${req.T('common:invalid')} access_type. ${req.T('api:must_one')} allow, deny.`) + .message(`${req.T('common.invalid')} access_type. ${req.T('api:must_one')} allow, deny.`) .api() if ( !['application', 'api_scope'].includes(req.body.target_type) ) return res.status(400) - .message(`${req.T('common:invalid')} target_type. ${req.T('api:must_one')} application, api_scope.`) + .message(`${req.T('common.invalid')} target_type. ${req.T('api:must_one')} application, api_scope.`) .api() // Make sure the target_id is valid @@ -122,13 +122,13 @@ class IAMController extends Controller { const app = await Application.findById(req.body.target_id) if ( !app || !app.active || !req.user.can(`app:${app.id}:view`) ) return res.status(400) - .message(`${req.T('common:invalid')} target_id.`) + .message(`${req.T('common.invalid')} target_id.`) .api() } else if ( req.body.target_type === 'api_scope' ) { const api_scopes = this.canon.get('controller::api:v1:Reflect.api_scopes')() if ( !api_scopes.includes(req.body.target_id) ) return res.status(400) - .message(`${req.T('common:invalid')} target_id.`) + .message(`${req.T('common.invalid')} target_id.`) .api() } @@ -152,25 +152,25 @@ class IAMController extends Controller { if ( !policy || !policy.active ) return res.status(404) - .message(req.T('iam:policy_not_found')) + .message(req.T('iam.policy_not_found')) .api() if ( !req.user.can(`iam:policy:${policy.id}:update`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['entity_type', 'entity_id', 'access_type', 'target_type', 'target_id'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`${req.T('api:missing_field')} ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } if ( !['user', 'group'].includes(req.body.entity_type) ) return res.status(400) - .message(`${req.T('common:invalid')} entity_type. ${req.T('api:must_one')} user, group.`) + .message(`${req.T('common.invalid')} entity_type. ${req.T('api.must_one')} user, group.`) .api() // Make sure the entity_id is valid @@ -179,25 +179,25 @@ class IAMController extends Controller { const user = await User.findById(req.body.entity_id) if ( !user || !req.user.can(`auth:user:${user.id}:view`) ) return res.status(400) - .message(`${req.T('common:invalid')} entity_id.`) + .message(`${req.T('common.invalid')} entity_id.`) .api() } else if ( req.body.entity_type === 'group' ) { const Group = this.models.get('auth:Group') const group = await Group.findById(req.body.entity_id) if ( !group || !group.active || !req.user.can(`auth:group:${group.id}:view`) ) return res.status(400) - .message(`${req.T('common:invalid')} entity_id.`) + .message(`${req.T('common.invalid')} entity_id.`) .api() } if ( !['allow', 'deny'].includes(req.body.access_type) ) return res.status(400) - .message(`${req.T('common:invalid')} access_type. ${req.T('api:must_one')} allow, deny.`) + .message(`${req.T('common.invalid')} access_type. ${req.T('api.must_one')} allow, deny.`) .api() if ( !['application', 'api_scope'].includes(req.body.target_type) ) return res.status(400) - .message(`${req.T('common:invalid')} target_type. ${req.T('api:must_one')} application, api_scope.`) + .message(`${req.T('common.invalid')} target_type. ${req.T('api.must_one')} application, api_scope.`) .api() // Make sure the target_id is valid @@ -206,13 +206,13 @@ class IAMController extends Controller { const app = await Application.findById(req.body.target_id) if ( !app || !app.active || !req.user.can(`app:${app.id}:view`) ) return res.status(400) - .message(`${req.T('common:invalid')} target_id.`) + .message(`${req.T('common.invalid')} target_id.`) .api() } else if ( req.body.target_type === 'api_scope' ) { const api_scopes = this.canon.get('controller::api:v1:Reflect.api_scopes')() if ( !api_scopes.includes(req.body.target_id) ) return res.status(400) - .message(`${req.T('common:invalid')} target_id.`) + .message(`${req.T('common.invalid')} target_id.`) .api() } @@ -231,12 +231,12 @@ class IAMController extends Controller { if ( !policy || !policy.active ) return res.status(404) - .message(req.T('iam:policy_not_found')) + .message(req.T('iam.policy_not_found')) .api() if ( !req.user.can(`iam:policy:${policy.id}:delete`) ) return res.status(401) - .message(req.T('api:insufficient_permissions')) + .message(req.T('api.insufficient_permissions')) .api() policy.active = false diff --git a/app/controllers/api/v1/LDAP.controller.js b/app/controllers/api/v1/LDAP.controller.js index e691791..ad8eeb2 100644 --- a/app/controllers/api/v1/LDAP.controller.js +++ b/app/controllers/api/v1/LDAP.controller.js @@ -52,12 +52,12 @@ class LDAPController extends Controller { if ( !client || !client.active ) return res.status(404) - .message('No client found with that ID.') + .message(req.T('api.client_not_found')) .api() if ( !req.user.can(`ldap:client:${client.id}:view`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() return res.api(await client.to_api()) @@ -69,12 +69,12 @@ class LDAPController extends Controller { if ( !group || !group.active ) return res.status(404) - .message('No group found with that ID.') + .message(req.T('api.group_not_found')) .api() if ( !req.user.can(`ldap:group:${group.id}:view`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() return res.api(await group.to_api()) @@ -83,7 +83,7 @@ class LDAPController extends Controller { async create_client(req, res, next) { if ( !req.user.can('ldap:client:create') ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() // validate inputs @@ -91,7 +91,7 @@ class LDAPController extends Controller { for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) } // Make sure the uid is free @@ -99,7 +99,7 @@ class LDAPController extends Controller { const existing_user = await User.findOne({ uid: req.body.uid }) if ( existing_user ) return res.status(400) - .message('A user with that uid already exists.') + .message(req.T('api.user_already_exists')) .api() // Verify password complexity @@ -107,7 +107,7 @@ class LDAPController extends Controller { const result = zxcvbn(req.body.password) if ( result.score < min_score ) return res.status(400) - .message(`Password does not meet the minimum complexity score of ${min_score}.`) + .message(req.T('auth.password_complexity_fail').replace('MIN_SCORE', min_score)) .api() // Create the client @@ -125,7 +125,7 @@ class LDAPController extends Controller { console.log(req.body) if ( !req.user.can(`ldap:group:create`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() // validate inputs @@ -133,7 +133,7 @@ class LDAPController extends Controller { for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } @@ -143,13 +143,13 @@ class LDAPController extends Controller { const existing_group = await Group.findOne({ name: req.body.name }) if ( existing_group ) return res.status(400) - .message('A group already exists with that name.') + .message(req.T('api.group_already_exists')) .api() // Make sure the role exists if ( !this.configs.get('auth.roles')[req.body.role] ) return res.status(400) - .message('Invalid role.') + .message(`${req.T('common.invalid')} role.`) .api() const group = new Group({ @@ -168,7 +168,7 @@ class LDAPController extends Controller { const user = await User.findById(user_id) if ( !user ) return res.status(400) - .message(`Invalid user ID: ${user_id}`) + .message(`${req.T('common.invalid')} user_id: ${user_id}`) .api() } @@ -185,19 +185,19 @@ class LDAPController extends Controller { if ( !client || !client.active ) return res.status(404) - .message('No client found with that ID.') + .message(req.T('api.client_not_found')) .api() if ( !req.user.can(`ldap:client:${client.id}:update`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['name', 'uid'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } @@ -216,7 +216,7 @@ class LDAPController extends Controller { const existing_user = await User.findOne({ uid: req.body.uid }) if ( existing_user ) return res.status(400) - .message('A user already exists with that uid.') + .message(req.T('api.user_already_exists')) .api() user.uid = req.body.uid @@ -229,7 +229,7 @@ class LDAPController extends Controller { const result = zxcvbn(req.body.password) if ( result.score < min_score ) return res.status(400) - .message(`Password does not meet the minimum complexity score of ${min_score}.`) + .message(req.T('auth.password_complexity_fail').replace('MIN_SCORE', min_score)) .api() await user.reset_password(req.body.password) @@ -247,19 +247,19 @@ class LDAPController extends Controller { const group = await Group.findById(req.params.id) if ( !group || !group.active ) return res.status(404) - .message('No group found with that ID.') + .message(req.T('api.group_not_found')) .api() if ( !req.user.can(`ldap:group:${group.id}:update`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['role', 'name'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } @@ -267,7 +267,7 @@ class LDAPController extends Controller { const existing_group = await Group.findOne({ name: req.body.name }) if ( existing_group && existing_group.id !== group.id ) return res.status(400) - .message('A group with that name already exists.') + .message(req.T('api.group_already_exists')) .api() group.name = req.body.name @@ -282,7 +282,7 @@ class LDAPController extends Controller { const user = await User.findById(user_id) if ( !user ) return res.status(400) - .message(`Invalid user_id: ${user_id}`) + .message(`${req.T('common.invalid')} user_id: ${user_id}`) .api() } @@ -301,12 +301,12 @@ class LDAPController extends Controller { if ( !client || !client.active ) return res.status(404) - .message('Client not found with that ID.') + .message(req.T('api.client_not_found')) .api() if ( !req.user.can(`ldap:client:${client.id}:delete`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() const user = await client.user() @@ -325,12 +325,12 @@ class LDAPController extends Controller { if ( !group || !group.active ) return res.status(404) - .message('No group found with that ID.') + .message(req.T('api.group_not_found')) .api() if ( !req.user.can(`ldap:group:${group.id}:delete`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() group.active = false diff --git a/app/controllers/api/v1/Message.controller.js b/app/controllers/api/v1/Message.controller.js index 99eb725..20cc2ec 100644 --- a/app/controllers/api/v1/Message.controller.js +++ b/app/controllers/api/v1/Message.controller.js @@ -24,14 +24,14 @@ class MessageController extends Controller { const banner_id = req.params.banner_id if ( !banner_id ) return res.status(400) - .message('Missing required parameter: banner_id') + .message(`${req.T('api.missing_field')} banner_id`) .api() const Message = this.models.get('Message') const message = await Message.findById(banner_id) if ( !message ) return res.status(404) - .message('Banner message not found with that ID.') + .message(req.T('api.banner_not_found')) .api() if ( message.user_id !== req.user.id ) diff --git a/app/controllers/api/v1/OAuth.controller.js b/app/controllers/api/v1/OAuth.controller.js index 18dced9..c9eafee 100644 --- a/app/controllers/api/v1/OAuth.controller.js +++ b/app/controllers/api/v1/OAuth.controller.js @@ -26,12 +26,12 @@ class OAuthController extends Controller { if ( !client || !client.active ) return res.status(404) - .message('Client not found with that ID.') + .message(req.T('api.client_not_found')) .api() if ( !req.user.can(`oauth:client:${client.id}:view`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() return res.api(await client.to_api()) @@ -40,26 +40,26 @@ class OAuthController extends Controller { async create_client(req, res, next) { if ( !req.user.can('oauth:client:create') ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['name', 'api_scopes', 'redirect_url'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } if ( !Array.isArray(req.body.api_scopes) ) { return res.status(400) - .message(`Improperly formatted field: api_scopes (should be array)`) + .message(`${req.T('api.improper_field')} api_scopes ${req.T('api.array')}`) .api() } if ( !is_absolute_url(req.body.redirect_url) ) return res.status(400) - .message(`Improperly formatted field: redirect_url (should be absolute URL)`) + .message(`${req.T('api.improper_field')} redirect_url ${req.T('api.absolute_url')}`) .api() const Client = this.models.get('oauth:Client') @@ -79,30 +79,30 @@ class OAuthController extends Controller { if ( !client || !client.active ) return res.status(404) - .message('Client not found with that ID.') + .message(req.T('api.client_not_found')) .api() if ( !req.user.can(`oauth:client:${client.id}:update`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['name', 'api_scopes', 'redirect_url'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } if ( !Array.isArray(req.body.api_scopes) ) return res.status(400) - .message(`Improperly formatted field: api_scopes (should be array)`) + .message(`${req.T('api.improper_field')} api_scopes ${req.T('api.array')}`) .api() if ( !is_absolute_url(req.body.redirect_url) ) return res.status(400) - .message(`Improperly formatted field: redirect_url (should be absolute URL)`) + .message(`${req.T('api.improper_field')} redirect_url ${req.T('api.absolute_url')}`) .api() client.name = req.body.name @@ -119,12 +119,12 @@ class OAuthController extends Controller { if ( !client || !client.active ) return res.status(404) - .message('Client not found with that ID.') + .message(req.T('api.client_not_found')) .api() if ( !req.user.can(`oauth:client:${client.id}:delete`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() client.active = false diff --git a/app/controllers/api/v1/Password.controller.js b/app/controllers/api/v1/Password.controller.js index 7225bd3..4800844 100644 --- a/app/controllers/api/v1/Password.controller.js +++ b/app/controllers/api/v1/Password.controller.js @@ -21,7 +21,7 @@ class PasswordController extends Controller { created: x.created, expires: x.expires, active: x.active, - name: x.name ?? '(unnamed)', + name: x.name ?? req.T('common.unnamed'), uuid: x.uuid, } })) @@ -30,7 +30,7 @@ class PasswordController extends Controller { async create_app_password(req, res, next) { if ( !req.body.name ) return res.status(400) - .message('Missing required field: name') + .message(`${req.T('api.missing_field')} name`) .api() const { password, record } = await req.user.app_password(req.body.name) @@ -46,13 +46,13 @@ class PasswordController extends Controller { async delete_app_password(req, res, next) { if ( !req.params.uuid ) return res.status(400) - .message('Missing required parameter: uuid') + .message(`${req.T('api.missing_field')} uuid`) .api() const match = req.user.app_passwords.filter(x => x.uuid === req.params.uuid)[0] if ( !match ) return res.status(400) - .message('App password not found with that UUID.') + .message(req.T('api.app_pw_not_found')) .api() req.user.app_passwords = req.user.app_passwords.filter(x => x.uuid !== req.params.uuid) @@ -63,7 +63,7 @@ class PasswordController extends Controller { async reset_password(req, res, next) { if ( !req.body.password ) return res.status(400) - .message('Missing required field: password') + .message(`${req.T('api.missing_field')} password`) .api() // Verify password complexity @@ -71,14 +71,14 @@ class PasswordController extends Controller { const result = zxcvbn(req.body.password) if ( result.score < min_score ) return res.status(400) - .message(`Password does not meet the minimum complexity score of ${min_score}.`) + .message(req.T('auth.password_complexity_fail').replace('MIN_SCORE', min_score)) .api() // Make sure it's not a re-do for ( const old_pw of req.user.password_resets ) { if ( await old_pw.check(req.body.password) ) { return res.status(400) - .message(`This password is a duplicate of one of your previous passwords.`) + .message(req.T('auth.duplicate_pw')) .api() } } @@ -99,7 +99,7 @@ class PasswordController extends Controller { async request_reset(req, res, next) { if ( !req.body.email ) return res.status(400) - .message('Missing required field: email') + .message(`${req.T('api.missing_field')} email`) .api() const User = this.models.get('auth:User') diff --git a/app/controllers/api/v1/Profile.controller.js b/app/controllers/api/v1/Profile.controller.js index a63b944..0b2e4f1 100644 --- a/app/controllers/api/v1/Profile.controller.js +++ b/app/controllers/api/v1/Profile.controller.js @@ -43,7 +43,7 @@ class ProfileController extends Controller { if ( !user ) return res.status(404) - .message('No user found with the specified ID.') + .message(req.T('api.user_not_found')) .api() // Make sure the required fields are provided @@ -51,14 +51,14 @@ class ProfileController extends Controller { for ( const field of required_fields ) { if ( !req.body[field]?.trim() ) return res.status(400) - .message(`Required field "${field}" is missing or invalid.`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } // Validate the e-mail if ( !Validator.validate(req.body.email) ) return res.status(400) - .message(`"email" field must be a valid e-mail address.`) + .message(`${req.T('api.improper_field')} email ${req.T('api.email')}`) .api() // Update the user's profile @@ -80,12 +80,12 @@ class ProfileController extends Controller { if ( !user ) return res.status(404) - .message('No user found with the specified ID.') + .message(req.T('api.user_not_found')) .api() if ( !req?.uploads?.photo ) return res.status(400) - .message('Missing required field: file') + .message(`${req.T('api.missing_field')} file`) .api() user.photo_file_id = req.uploads.photo.id @@ -101,7 +101,7 @@ class ProfileController extends Controller { if ( !user ) return res.status(404) - .message('No user found with the specified ID.') + .message(req.T('api.user_not_found')) .api() const photo = await user.photo() diff --git a/app/controllers/api/v1/Reflect.controller.js b/app/controllers/api/v1/Reflect.controller.js index 97c2ef3..89eb789 100644 --- a/app/controllers/api/v1/Reflect.controller.js +++ b/app/controllers/api/v1/Reflect.controller.js @@ -17,7 +17,7 @@ class ReflectController extends Controller { const data = [] for ( const token of tokens ) { const client = await Client.findOne({ uuid: token.clientID }) - let client_display = client && client.active ? client.name : '(Non-existent Client)' + let client_display = client && client.active ? client.name : req.T('api.nonexistent_client') data.push({ id: token.id, @@ -38,7 +38,7 @@ class ReflectController extends Controller { if ( !token || token.userID !== req.user.id || token.expires <= new Date ) return res.status(404) - .message('Token not found with that ID, or expired.') + .message(req.T('api.token_not_found')) .api() return res.api({ @@ -55,19 +55,19 @@ class ReflectController extends Controller { if ( !req.body.client_id ) return res.status(400) - .message('Missing required field: client_id') + .message(`${req.T('api.missing_field')} client_id`) .api() const Client = this.models.get('oauth:Client') const client = await Client.findOne({uuid: req.body.client_id}) if ( !client || !client.active ) return res.status(400) - .message('Invalid client_id.') + .message(`${req.T('common.invalid')} client_id.`) .api() if ( !req.user.can(`oauth:client:${client.id}:view`) ) return res.status(401) - .message('Insufficient permissions.') + .message(req.T('api.insufficient_permissions')) .api() const expires = new Date() @@ -96,12 +96,12 @@ class ReflectController extends Controller { if ( !token || token.userID !== req.user.id || token.expires <= new Date ) return res.status(404) - .message('Token not found with that ID, or expired.') + .message(req.T('api.token_not_found')) .api() if ( !req.body.client_id ) return res.status(400) - .message('Missing required field: client_id') + .message(`${req.T('api.missing_field')} client_id`) .api() const Client = this.models.get('oauth:Client') @@ -109,7 +109,7 @@ class ReflectController extends Controller { if ( !client || !client.active || !req.user.can(`oauth:client:${client.id}:view`) ) return res.status(400) - .message('Invalid client_id.') + .message(`${req.T('common.invalid')} client_id.`) .api() token.client_id = client.uuid @@ -123,7 +123,7 @@ class ReflectController extends Controller { if ( !token || token.userID !== req.user.id || token.expires <= new Date ) return res.status(404) - .message('Token not found with that ID, or expired.') + .message(req.T('api.token_not_found')) .api() await token.delete() @@ -171,7 +171,7 @@ class ReflectController extends Controller { async check_permissions(req, res, next) { if ( !req.body.permissions ) return res.status(400) - .message('Missing permissions to check.') + .message(`${req.T('api.missing_field')} permissions`) .api() const parsed = typeof req.body.permissions === 'string' ? this.utility.infer(req.body.permissions) : req.body.permissions diff --git a/app/controllers/api/v1/SAML.controller.js b/app/controllers/api/v1/SAML.controller.js index e53d279..9823060 100644 --- a/app/controllers/api/v1/SAML.controller.js +++ b/app/controllers/api/v1/SAML.controller.js @@ -34,7 +34,7 @@ class SAMLController extends Controller { for ( const field of required_fields ) { if ( !req.body[field]?.trim() ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } @@ -46,7 +46,7 @@ class SAMLController extends Controller { if ( existing_provider ) return res.status(400) - .send(`A service provider with that entity_id already exists.`) + .send(req.T('api.provider_already_exists')) .api() const data = { @@ -82,7 +82,7 @@ class SAMLController extends Controller { for ( const field of required_fields ) { if ( !req.body[field].trim() ) return res.status(400) - .message(`Missing required field: ${field}`) + .message(`${req.T('api.missing_field')} ${field}`) .api() } @@ -94,7 +94,7 @@ class SAMLController extends Controller { if ( duplicate_providers.length > 0 ) return res.status(400) - .message('A service provider already exists with that entity_id.') + .message(api.T('api.provider_already_exists')) .api() // Update the record diff --git a/app/controllers/api/v1/Settings.controller.js b/app/controllers/api/v1/Settings.controller.js index a46f018..7395c33 100644 --- a/app/controllers/api/v1/Settings.controller.js +++ b/app/controllers/api/v1/Settings.controller.js @@ -23,7 +23,7 @@ class SettingsController extends Controller { if ( !setting ) return res.status(404) - .message('No setting exists with that key.') + .message(req.T('api.setting_not_found')) .api() return res.api(await setting.to_api()) @@ -35,7 +35,7 @@ class SettingsController extends Controller { if ( !setting ) return res.status(404) - .message('No setting exists with that key.') + .message(req.T('api.setting_not_found')) .api() setting.set(req.body.value) diff --git a/app/controllers/auth/Forms.controller.js b/app/controllers/auth/Forms.controller.js index 92d16f9..7aaca2d 100644 --- a/app/controllers/auth/Forms.controller.js +++ b/app/controllers/auth/Forms.controller.js @@ -21,7 +21,7 @@ class Forms extends FormController { return res.page('auth:login', { ...this.Vue.data({ - login_message: req.session?.auth?.message || 'Please sign-in to continue.', + login_message: req.session?.auth?.message || req.T('auth.sign_in_to_continue'), registration_enabled: await Setting.get('auth.allow_registration') }), }) @@ -29,7 +29,7 @@ class Forms extends FormController { async logout_provider_present_success(req, res, next) { return this.Vue.auth_message(res, { - message: 'You have been successfully logged out.', + message: req.T('auth.logged_out'), next_destination: '/', }) } diff --git a/app/controllers/auth/MFA.controller.js b/app/controllers/auth/MFA.controller.js index 3e09b68..4d36853 100644 --- a/app/controllers/auth/MFA.controller.js +++ b/app/controllers/auth/MFA.controller.js @@ -9,8 +9,8 @@ class MFAController extends Controller { if ( req.user.mfa_enabled ) { // Already set up! return this.Vue.auth_message(res, { - message: 'It looks like your account is already set up for multi-factor authentication. Unable to continue with MFA setup.', - next_destination: '/', // TODO update this + message: req.T('auth.already_mfa'), + next_destination: '/dash/profile', button_text: 'Okay', }) } @@ -24,7 +24,7 @@ class MFAController extends Controller { async challenge(req, res, next) { if ( !req.user.mfa_enabled ) { return this.Vue.auth_message(res, { - message: 'Your account is not configured to use multi-factor authentication. Would you like to configure it now?', + message: req.T('auth.mfa_prompt'), next_destination: '/auth/mfa/setup', button_text: 'Setup MFA', }) @@ -42,7 +42,7 @@ class MFAController extends Controller { async get_disable(req, res, next) { return this.Vue.confirm(res, { - message: `You are about to disable multi-factor authentication for your account. This process will require you to re-authenticate to continue.

Proceed?`, + message: req.T('auth.mfa_disable_prompt'), yes: '/auth/mfa/disable/process', no: '/dash/profile', }) @@ -61,7 +61,7 @@ class MFAController extends Controller { || !Array.isArray(req.user.mfa_token.recovery_codes) || req.user.mfa_token.recovery_codes.length < 1 ) return this.Vue.auth_message(res, { - message: 'Unfortunately, it looks like your account does not have any MFA recovery codes generated.', + message: req.T('auth.mfa_no_recovery'), next_destination: '/auth/mfa/challenge', button_text: 'Go Back', }) diff --git a/app/controllers/auth/Oauth2.controller.js b/app/controllers/auth/Oauth2.controller.js index f018e3e..1cfa137 100644 --- a/app/controllers/auth/Oauth2.controller.js +++ b/app/controllers/auth/Oauth2.controller.js @@ -13,7 +13,7 @@ class Oauth2 extends Oauth2Controller { async authorize_post(req, res, next) { const client = await this._get_authorize_client({query: req.body}) - if ( !client ) return this._uniform(res, 'Unable to authorize client application. The application config is invalid. Please check the client ID and redirect URI and try again.') + if ( !client ) return this._uniform(res, req.T('unable_to_authorize')) const StarshipClient = this.models.get('oauth:Client') const starship_client = await StarshipClient.findOne({ active: true, uuid: client.clientID }) @@ -25,7 +25,7 @@ class Oauth2 extends Oauth2Controller { async authorize_get(req, res, next) { const client = await this._get_authorize_client(req) - if ( !client ) return this._uniform(res, 'Unable to authorize client application. The application config is invalid. Please check the client ID and redirect URI and try again.') + if ( !client ) return this._uniform(res, req.T('unable_to_authorize')) const uri = new URL(req.query.redirect_uri) const StarshipClient = this.models.get('oauth:Client') @@ -46,18 +46,18 @@ class Oauth2 extends Oauth2Controller { ...this.Vue.data({ message: `

Authorize ${client.name}?


-${client.name} is requesting access to your ${this.configs.get('app.name')} account. Once you grant it, you may not be prompted for permission again. +${req.T('auth.oauth_prompt').replace('CLIENT_NAME', client.name).replace('APP_NAME', this.configs.get('app.name'))}


-You will be redirected to: ${uri.host}`, +${req.T('auth.will_redirect')} ${uri.host}`, actions: [ { - text: 'Deny', + text: req.T('common.deny'), action: 'redirect', next: '/dash', }, { - text: 'Grant Access', + text: req.T('common.grant'), action: 'post', params: { redirect_uri: uri.toString(), diff --git a/app/controllers/auth/Trust.controller.js b/app/controllers/auth/Trust.controller.js index 3e67375..81a795e 100644 --- a/app/controllers/auth/Trust.controller.js +++ b/app/controllers/auth/Trust.controller.js @@ -13,7 +13,7 @@ class TrustController extends Controller { */ async get_issue(req, res, next) { if ( !req.trust.has_flow() ) - return res.status(400).message('Missing trust flow data.').send() + return res.status(400).message(req.T('auth.missing_trust_flow')).send() // Check if the session already has a token for this scope const has_scope = req.trust.has(req.trust.flow_scope()) @@ -28,22 +28,11 @@ class TrustController extends Controller { return res.page('auth:trust:grant', { ...this.Vue.data({ grant_code: token, - login_message: 'Please re-authenticate to continue.', + login_message: req.T('auth.reauth_to_continue'), }), ...this.Vue.session(req) }) } - - /*async get_continue(req, res, next) { - if ( !req.trust.has_flow() ) - return res.status(400).message('Missing trust flow data.') - - if ( !req.trust.in_progress() ) - return res.status(401).message('No flow in progress. Please try again.') - - req.trust.grant(req.trust.flow_scope()) - return res.redirect(req.trust.end()) - }*/ } module.exports = exports = TrustController diff --git a/app/controllers/dash/Groups.controller.js b/app/controllers/dash/Groups.controller.js deleted file mode 100644 index 3ab7f0b..0000000 --- a/app/controllers/dash/Groups.controller.js +++ /dev/null @@ -1,35 +0,0 @@ -const { Controller } = require('libflitter') - -class GroupsController extends Controller { - static get services() { - return [...super.services, 'cobalt', 'models'] - } - - async get_listing(req, res, next) { - const Group = this.models.get('ldap:Group') - const groups = await Group.find() - const formatted = groups.map(x => { - return { - name: x.name, - count: x.user_ids.length, - } - }) - - return this.cobalt.listing(req, res, { - title: 'LDAP Groups', // TODO generalize this for SAML/OAuth2 - columns: [ - { - name: 'Group Name', - field: 'name', - }, - { - name: '# Users', - field: 'count', - }, - ], - data: formatted, - }) - } -} - -module.exports = exports = GroupsController diff --git a/app/controllers/dash/SAML.controller.js b/app/controllers/dash/SAML.controller.js deleted file mode 100644 index 3b9c0f5..0000000 --- a/app/controllers/dash/SAML.controller.js +++ /dev/null @@ -1,78 +0,0 @@ -const { Controller } = require('libflitter') - -class SAMLController extends Controller { - static get services() { - return [...super.services, 'cobalt'] - } - - async get_sp_listing(req, res, next) { - return this.cobalt.listing(req, res, { - title: 'SAML Service Providers', - resource: 'saml/Provider', - columns: [ - { - name: 'Provider Name', - field: 'name', - }, - { - name: 'Entity ID', - field: 'entity_id', - }, - { - name: 'Has SLO?', - field: 'has_slo', - renderer: 'boolean', - }, - { - name: 'ACS URL', - field: 'acs_url', - }, - ], - actions: [ - { - type: 'resource', - position: 'main', - action: 'insert', - text: 'Create New', - color: 'success', - }, - ], - }) - } - - async get_sp_form(req, res, next) { - return this.cobalt.form(req, res, { - item: 'SAML Service Provider', - plural: 'SAML Service Providers', - resource: 'saml/Provider', - ...(req.params.id ? { existing_id: req.params.id } : {}), - fields: [ - { - name: 'Provider Name', - field: 'name', - placeholder: 'Awesome External App', - required: true, - }, - { - name: 'Entity ID', - field: 'entity_id', - placeholder: 'https://my.awesome.app/saml/metadata.xml', - required: true, - }, - { - name: 'Assertion Consumer Service URL', - field: 'acs_url', - placeholder: 'https://my.awesome.app/saml/acs', - required: true, - }, - { - name: 'Single-Logout URL', - field: 'slo_url', - placeholder: 'https://my.awesome.app/saml/logout', - }, - ], - }) - } -} - -module.exports = exports = SAMLController diff --git a/app/controllers/dash/Users.controller.js b/app/controllers/dash/Users.controller.js deleted file mode 100644 index 81b6cd6..0000000 --- a/app/controllers/dash/Users.controller.js +++ /dev/null @@ -1,47 +0,0 @@ -const { Controller } = require('libflitter') - -class UsersController extends Controller { - static get services() { - return [...super.services, 'models', 'cobalt'] - } - - async get_listing(req, res, next) { - // Columns: Username, First, Last, E-Mail - const User = this.models.get('auth:User') - const users = await User.find() - const formatted = users.map(x => { - return { - username: x.uid, - first: x.first_name, - last: x.last_name, - email: x.email, - } - }) - - return this.cobalt.listing(req, res, { - title: 'Users', - columns: [ - { - name: 'Username', - field: 'username', - }, - { - name: 'First Name', - field: 'first', - }, - { - name: 'Last Name', - field: 'last', - }, - { - name: 'E-Mail Address', - field: 'email', - }, - ], - data: formatted, - }) - } - -} - -module.exports = exports = UsersController diff --git a/app/controllers/saml/SAML.controller.js b/app/controllers/saml/SAML.controller.js index e2c64f8..b6c9808 100644 --- a/app/controllers/saml/SAML.controller.js +++ b/app/controllers/saml/SAML.controller.js @@ -32,7 +32,7 @@ class SAMLController extends Controller { const can_access = await Policy.check_user_access(req.user, associated_app.id) if ( !can_access ) { return this.Vue.auth_message(res, { - message: `Sorry, you don't have permission to access this application. Please ask your administrator to grant you access to ${associated_app.name}.`, + message: req.T('saml.no_access').replace('APP_NAME', associated_app.name), next_destination: '/dash', }) } @@ -43,7 +43,7 @@ class SAMLController extends Controller { cert: await this.saml.public_cert(), key: await this.saml.private_key(), getPostURL: (wtrealm, wreply, req, callback) => { - this.output.debug(`SAML Redirect URL: ${req.saml_request.service_provider.acs_url}`) + this.output.debug(`${req.T('saml.redirect_url')} ${req.saml_request.service_provider.acs_url}`) return callback(null, req.saml_request.service_provider.acs_url) // fetch this from registered SAML app }, profileMapper: user => new FlitterProfileMapper(user), @@ -68,19 +68,19 @@ class SAMLController extends Controller { key: await this.saml.private_key(), protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', clearIdPSession: done => { - this.output.info(`Clearing IdP session for user: ${req.user.uid}`) + this.output.info(`${req.T('saml.clear_idp_session')} ${req.user.uid}`) req.saml.participants.clear().then(async () => { if ( this.saml.config().slo.end_coreid_session ) { await req.user.get_provider().logout(req) // show logout page return this.Vue.auth_message(res, { - message: `You have been successfully logged out from ${this.configs.get('app.name')}.`, + message: req.T('auth.logged_out'), next_destination: '/', }) } else { return this.Vue.auth_message(res, { - message: `You have been successfully logged out.`, + message: req.T('auth.logged_out'), next_destination: '/', }) } diff --git a/app/routing/routers/dash/groups.routes.js b/app/routing/routers/dash/groups.routes.js deleted file mode 100644 index da5ea07..0000000 --- a/app/routing/routers/dash/groups.routes.js +++ /dev/null @@ -1,13 +0,0 @@ -const groups_routes = { - prefix: '/dash/groups', - - middleware: [ - 'auth:UserOnly', - ], - - get: { - '/': [ 'controller::dash:Groups.get_listing' ] - }, -} - -module.exports = exports = groups_routes diff --git a/app/routing/routers/dash/saml.routes.js b/app/routing/routers/dash/saml.routes.js deleted file mode 100644 index aaeef31..0000000 --- a/app/routing/routers/dash/saml.routes.js +++ /dev/null @@ -1,13 +0,0 @@ -const groups_routes = { - prefix: '/dash/saml', - - middleware: [ - 'auth:UserOnly', - ], - - get: { - '/service-providers': [ 'controller::dash:SAML.get_sp_listing' ] - }, -} - -module.exports = exports = groups_routes diff --git a/app/routing/routers/dash/users.routes.js b/app/routing/routers/dash/users.routes.js deleted file mode 100644 index 81ee28f..0000000 --- a/app/routing/routers/dash/users.routes.js +++ /dev/null @@ -1,13 +0,0 @@ -const user_routes = { - prefix: '/dash/users', - - middleware: [ - 'auth:UserOnly', - ], - - get: { - '/': [ 'controller::dash:Users.get_listing' ] - }, -} - -module.exports = exports = user_routes diff --git a/locale/en_US/api.locale.js b/locale/en_US/api.locale.js index 87f0f04..7e8732e 100644 --- a/locale/en_US/api.locale.js +++ b/locale/en_US/api.locale.js @@ -6,6 +6,20 @@ module.exports = exports = { group_already_exists: 'A group with that name already exists.', user_not_found: 'User not found with that ID.', + user_already_exists: 'A user with that identifier already exists.', + + client_not_found: 'Client not found with that ID.', + nonexistent_client: '(Non-existent Client)', + + token_not_found: 'Token not found with that ID, or the token has expired.', + + provider_already_exists: 'A service provider with that entity_id already exists.', + + setting_not_found: 'No such setting exists with that key.', + + banner_not_found: 'Banner message not found with that ID.', + + app_pw_not_found: 'App password not found with that UUID.', invalid_ldap_client_id: 'Invalid ldap_client_id:', invalid_oauth_client_id: 'Invalid oauth_client_id:', @@ -18,6 +32,9 @@ module.exports = exports = { }, improper_field: 'Improperly formatted field:', alphanum_underscores: '(alphanumeric/underscores)', + array: '(array)', + email: '(email)', + absolute_url: '(should be absolute URL)', provide_one: 'Please provide one of:', must_one: 'Must be one of:', } diff --git a/locale/en_US/auth.locale.js b/locale/en_US/auth.locale.js index 4fbef18..ee4a3a8 100644 --- a/locale/en_US/auth.locale.js +++ b/locale/en_US/auth.locale.js @@ -5,10 +5,25 @@ module.exports = exports = { no_mfa: 'Your user is not configured to use MFA.', already_has_mfa: 'MFA is already configured for your user.', password_complexity_fail: 'Password does not meet the minimum complexity score of MIN_SCORE.', + duplicate_pw: 'This password is a duplicate of one of your previous passwords.', invalid_trap: 'Invalid trap type.', unable_to_grant_trust: 'Unable to grant trust. Grant token is invalid.', + missing_trust_flow: 'Missing trust flow data.', invalid_un_or_pw: 'Invalid username or password.', unable_to_complete: 'Unable to complete authentication: one or more errors occurred', + + sign_in_to_continue: 'Please, sign-in to continue.', + logged_out: 'You have been suggessfully logged out.', + + already_mfa: 'It looks like your account is already set up for multi-factor authentication. Unable to continue with MFA setup.', + mfa_prompt: 'Your account is not configured to use multi-factor authentication. Would you like to configure it now?', + mfa_disable_prompt: 'You are about to disable multi-factor authentication for your account. This process will require you to re-authenticate to continue.

Proceed?', + mfa_no_recovery: 'Unfortunately, it looks like your account does not have any MFA recovery codes generated.', + unable_to_authorize: 'Unable to authorize client application. The application config is invalid. Please check the client ID and redirect URI and try again.', + + oauth_prompt: 'CLIENT_NAME is requesting access to your APP_NAME account. Once you grant it, you may not be prompted for permission again.', + will_redirect: 'You will be redirected to:', + reauth_to_continue: 'Please re-authenticate to continue.', } diff --git a/locale/en_US/common.locale.js b/locale/en_US/common.locale.js index 7993850..65f5eda 100644 --- a/locale/en_US/common.locale.js +++ b/locale/en_US/common.locale.js @@ -6,4 +6,8 @@ module.exports = exports = { log_out: 'Log out', invalid: 'Invalid', + unnamed: '(unnamed)', + + deny: 'Deny', + grant: 'Grant Access', } diff --git a/locale/en_US/saml.locale.js b/locale/en_US/saml.locale.js new file mode 100644 index 0000000..4dfd760 --- /dev/null +++ b/locale/en_US/saml.locale.js @@ -0,0 +1,5 @@ +module.exports = exports = { + no_access: 'Sorry, you don\'t have permission to access this application. Please ask your administrator to grant you access to APP_NAME.', + redirect_url: 'SAML Redirect URL:', + clear_idp_session: 'Clearing IdP session for user:' +}