Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
60003d64d5
|
|||
|
535dde13ff
|
|||
|
63d102296f
|
|||
|
77d203b2b0
|
|||
|
fcbf25e3ce
|
|||
|
084ec7bbc1
|
|||
|
6b3339a883
|
|||
|
8f1bbfef56
|
|||
|
e400e16ccc
|
|||
|
97096f619f
|
|||
|
2d97b77bbf
|
|||
|
5916222f7b
|
|||
|
bb79d52911
|
|||
|
2e05ec77c8
|
|||
|
433af8261f
|
35
app/assets/error-log.js
Normal file
35
app/assets/error-log.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
window.COREID_ERROR_LOG_URL = window.COREID_ERROR_LOG_URL || '/api/v1/log-error'
|
||||||
|
|
||||||
|
async function logError(error) {
|
||||||
|
try {
|
||||||
|
await fetch(window.COREID_ERROR_LOG_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
full_url: window.location.href,
|
||||||
|
trace: [
|
||||||
|
error.name + ': ' + error.message,
|
||||||
|
error.stack,
|
||||||
|
].join('\n')
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
;(function() {
|
||||||
|
var old_onerror = window.onerror
|
||||||
|
|
||||||
|
window.onerror = function(msg, src, line, col, error) {
|
||||||
|
logError(error).then(function() {
|
||||||
|
if ( typeof old_onerror === 'function' ) {
|
||||||
|
try {
|
||||||
|
old_onerror(msg, src, line, col, error)
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
||||||
@@ -18,6 +18,11 @@ class CoreIDAdapter {
|
|||||||
expiresAt = new Date(Date.now() + (expiresIn * 1000))
|
expiresAt = new Date(Date.now() + (expiresIn * 1000))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( payload.uid ) {
|
||||||
|
payload.originalUid = payload.uid
|
||||||
|
payload.uid = payload.uid.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
await this.coll().updateOne(
|
await this.coll().updateOne(
|
||||||
{ _id },
|
{ _id },
|
||||||
{ $set: { payload, ...(expiresAt ? { expiresAt } : undefined) } },
|
{ $set: { payload, ...(expiresAt ? { expiresAt } : undefined) } },
|
||||||
@@ -34,6 +39,11 @@ class CoreIDAdapter {
|
|||||||
).limit(1).next()
|
).limit(1).next()
|
||||||
|
|
||||||
if (!result) return undefined
|
if (!result) return undefined
|
||||||
|
|
||||||
|
if ( result?.payload?.originalUid ) {
|
||||||
|
result.payload.uid = result.payload.originalUid
|
||||||
|
}
|
||||||
|
|
||||||
return result.payload
|
return result.payload
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,11 +59,16 @@ class CoreIDAdapter {
|
|||||||
|
|
||||||
async findByUid(uid) {
|
async findByUid(uid) {
|
||||||
const result = await this.coll().find(
|
const result = await this.coll().find(
|
||||||
{ 'payload.uid': uid },
|
{ 'payload.uid': uid.toLowerCase() },
|
||||||
{ payload: 1 },
|
{ payload: 1 },
|
||||||
).limit(1).next()
|
).limit(1).next()
|
||||||
|
|
||||||
if (!result) return undefined
|
if (!result) return undefined
|
||||||
|
|
||||||
|
if ( result?.payload?.originalUid ) {
|
||||||
|
result.payload.uid = result.payload.originalUid
|
||||||
|
}
|
||||||
|
|
||||||
return result.payload
|
return result.payload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class FlitterProfileMapper {
|
|||||||
getClaims() {
|
getClaims() {
|
||||||
const claims = {}
|
const claims = {}
|
||||||
|
|
||||||
claims[this.map.nameIdentifier] = this.user.uid
|
claims[this.map.nameIdentifier] = this.user.uid.toLowerCase()
|
||||||
claims[this.map.email] = this.user.email
|
claims[this.map.email] = this.user.email
|
||||||
claims[this.map.name] = `${this.user.first_name} ${this.user.last_name}`
|
claims[this.map.name] = `${this.user.first_name} ${this.user.last_name}`
|
||||||
claims[this.map.givenname] = this.user.first_name
|
claims[this.map.givenname] = this.user.first_name
|
||||||
@@ -54,7 +54,7 @@ class FlitterProfileMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNameIdentifier() {
|
getNameIdentifier() {
|
||||||
return { nameIdentifier: this.user.uid }
|
return { nameIdentifier: this.user.uid.toLowerCase() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ class Home extends Controller {
|
|||||||
async tmpl(req, res) {
|
async tmpl(req, res) {
|
||||||
return res.page('tmpl', {...this.Vue.data(), ...this.Vue.session(req)})
|
return res.page('tmpl', {...this.Vue.data(), ...this.Vue.session(req)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async log_front_end_error(req, res, next) {
|
||||||
|
const FrontEndError = this.models.get('FrontEndError')
|
||||||
|
await FrontEndError.log(req)
|
||||||
|
return res.api()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Home
|
module.exports = Home
|
||||||
|
|||||||
@@ -119,14 +119,12 @@ class OpenIDController extends Controller {
|
|||||||
uid, prompt, params, session,
|
uid, prompt, params, session,
|
||||||
} = await this.openid_connect.provider.interactionDetails(req, res)
|
} = await this.openid_connect.provider.interactionDetails(req, res)
|
||||||
|
|
||||||
console.log({uid, prompt, params, session})
|
|
||||||
|
|
||||||
const name = prompt.name
|
const name = prompt.name
|
||||||
if ( typeof this[name] !== 'function' ) {
|
if ( typeof this[name] !== 'function' ) {
|
||||||
return this.fail(res, 'Sorry, something has gone wrong.')
|
return this.fail(res, 'Sorry, something has gone wrong.')
|
||||||
}
|
}
|
||||||
|
|
||||||
return this[name](req, res, { uid, prompt, params, session })
|
return this[name](req, res, { uid: uid.toLowerCase(), prompt, params, session })
|
||||||
}
|
}
|
||||||
|
|
||||||
async consent(req, res, { uid, prompt, params, session }) {
|
async consent(req, res, { uid, prompt, params, session }) {
|
||||||
@@ -142,13 +140,13 @@ class OpenIDController extends Controller {
|
|||||||
const Policy = this.models.get('iam:Policy')
|
const Policy = this.models.get('iam:Policy')
|
||||||
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
||||||
if ( !application ) {
|
if ( !application ) {
|
||||||
this.output.warning('IAM Denial!')
|
this.output.warn('IAM Denial!')
|
||||||
return this.Vue.auth_message(res, {
|
return this.Vue.auth_message(res, {
|
||||||
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
||||||
next_destination: '/dash',
|
next_destination: '/dash',
|
||||||
})
|
})
|
||||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||||
this.output.warning('IAM Denial!')
|
this.output.warn('IAM Denial!')
|
||||||
return this.Vue.auth_message(res, {
|
return this.Vue.auth_message(res, {
|
||||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||||
next_destination: '/dash',
|
next_destination: '/dash',
|
||||||
@@ -172,7 +170,7 @@ class OpenIDController extends Controller {
|
|||||||
{
|
{
|
||||||
text: req.T('common.grant'),
|
text: req.T('common.grant'),
|
||||||
action: 'redirect',
|
action: 'redirect',
|
||||||
next: `/openid/interaction/${uid}/grant`,
|
next: `/openid/interaction/${uid.toLowerCase()}/grant`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@@ -180,7 +178,7 @@ class OpenIDController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async login(req, res, { uid, prompt, params, session }) {
|
async login(req, res, { uid, prompt, params, session }) {
|
||||||
return res.redirect(`/openid/interaction/${uid}/start-session`)
|
return res.redirect(`/openid/interaction/${uid.toLowerCase()}/start-session`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,13 +200,13 @@ class OpenIDController extends Controller {
|
|||||||
const Policy = this.models.get('iam:Policy')
|
const Policy = this.models.get('iam:Policy')
|
||||||
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
||||||
if ( !application ) {
|
if ( !application ) {
|
||||||
this.output.warning('IAM Denial!')
|
this.output.warn('IAM Denial!')
|
||||||
return this.Vue.auth_message(res, {
|
return this.Vue.auth_message(res, {
|
||||||
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
||||||
next_destination: '/dash',
|
next_destination: '/dash',
|
||||||
})
|
})
|
||||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||||
this.output.warning('IAM Denial!')
|
this.output.warn('IAM Denial!')
|
||||||
return this.Vue.auth_message(res, {
|
return this.Vue.auth_message(res, {
|
||||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||||
next_destination: '/dash',
|
next_destination: '/dash',
|
||||||
@@ -238,13 +236,13 @@ class OpenIDController extends Controller {
|
|||||||
const Policy = this.models.get('iam:Policy')
|
const Policy = this.models.get('iam:Policy')
|
||||||
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
||||||
if ( !application ) {
|
if ( !application ) {
|
||||||
this.output.warning('IAM Denial!')
|
this.output.warn('IAM Denial!')
|
||||||
return this.Vue.auth_message(res, {
|
return this.Vue.auth_message(res, {
|
||||||
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
||||||
next_destination: '/dash',
|
next_destination: '/dash',
|
||||||
})
|
})
|
||||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||||
this.output.warning('IAM Denial!')
|
this.output.warn('IAM Denial!')
|
||||||
return this.Vue.auth_message(res, {
|
return this.Vue.auth_message(res, {
|
||||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||||
next_destination: '/dash',
|
next_destination: '/dash',
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class AuthController extends Controller {
|
|||||||
const user = new User({
|
const user = new User({
|
||||||
first_name: req.body.first_name,
|
first_name: req.body.first_name,
|
||||||
last_name: req.body.last_name,
|
last_name: req.body.last_name,
|
||||||
uid: req.body.uid,
|
uid: req.body.uid.toLowerCase(),
|
||||||
email: req.body.email,
|
email: req.body.email,
|
||||||
trap: 'password_reset', // Force user to reset password
|
trap: 'password_reset', // Force user to reset password
|
||||||
})
|
})
|
||||||
@@ -297,7 +297,7 @@ class AuthController extends Controller {
|
|||||||
.api()
|
.api()
|
||||||
|
|
||||||
const user = new User({
|
const user = new User({
|
||||||
uid: req.body.uid,
|
uid: req.body.uid.toLowerCase(),
|
||||||
email: req.body.email,
|
email: req.body.email,
|
||||||
first_name: req.body.first_name,
|
first_name: req.body.first_name,
|
||||||
last_name: req.body.last_name,
|
last_name: req.body.last_name,
|
||||||
@@ -417,7 +417,7 @@ class AuthController extends Controller {
|
|||||||
|
|
||||||
user.first_name = req.body.first_name
|
user.first_name = req.body.first_name
|
||||||
user.last_name = req.body.last_name
|
user.last_name = req.body.last_name
|
||||||
user.uid = req.body.uid
|
user.uid = req.body.uid.toLowerCase()
|
||||||
user.email = req.body.email
|
user.email = req.body.email
|
||||||
|
|
||||||
if ( req.body.tagline )
|
if ( req.body.tagline )
|
||||||
@@ -493,7 +493,7 @@ class AuthController extends Controller {
|
|||||||
|
|
||||||
if ( is_valid ) {
|
if ( is_valid ) {
|
||||||
const User = this.models.get('auth:User')
|
const User = this.models.get('auth:User')
|
||||||
const user = await User.findOne({uid: req.body.username})
|
const user = await User.findOne({uid: req.body.username.toLowerCase()})
|
||||||
if ( !user || !user.can_login ) is_valid = false
|
if ( !user || !user.can_login ) is_valid = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,7 +511,7 @@ class AuthController extends Controller {
|
|||||||
const data = {}
|
const data = {}
|
||||||
if ( req.body.username ) {
|
if ( req.body.username ) {
|
||||||
const existing_user = await User.findOne({
|
const existing_user = await User.findOne({
|
||||||
uid: req.body.username,
|
uid: req.body.username.toLowerCase(),
|
||||||
})
|
})
|
||||||
|
|
||||||
data.username_taken = !!existing_user
|
data.username_taken = !!existing_user
|
||||||
@@ -544,7 +544,8 @@ class AuthController extends Controller {
|
|||||||
.message(req.T('auth.unable_to_complete'))
|
.message(req.T('auth.unable_to_complete'))
|
||||||
.api({ errors })
|
.api({ errors })
|
||||||
|
|
||||||
const login_args = await flitter.get_login_args(req.body)
|
const [username, ...other_args] = await flitter.get_login_args(req.body)
|
||||||
|
const login_args = [username.toLowerCase(), ...other_args]
|
||||||
const user = await flitter.login.apply(flitter, login_args)
|
const user = await flitter.login.apply(flitter, login_args)
|
||||||
|
|
||||||
if ( !user )
|
if ( !user )
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class LDAPController extends Controller {
|
|||||||
|
|
||||||
// Make sure the uid is free
|
// Make sure the uid is free
|
||||||
const User = this.models.get('auth:User')
|
const User = this.models.get('auth:User')
|
||||||
const existing_user = await User.findOne({ uid: req.body.uid })
|
const existing_user = await User.findOne({ uid: req.body.uid.toLowerCase() })
|
||||||
if ( existing_user )
|
if ( existing_user )
|
||||||
return res.status(400)
|
return res.status(400)
|
||||||
.message(req.T('api.user_already_exists'))
|
.message(req.T('api.user_already_exists'))
|
||||||
@@ -113,7 +113,7 @@ class LDAPController extends Controller {
|
|||||||
// Create the client
|
// Create the client
|
||||||
const Client = this.models.get('ldap:Client')
|
const Client = this.models.get('ldap:Client')
|
||||||
const client = await Client.create({
|
const client = await Client.create({
|
||||||
uid: req.body.uid,
|
uid: req.body.uid.toLowerCase(),
|
||||||
password: req.body.password,
|
password: req.body.password,
|
||||||
name: req.body.name,
|
name: req.body.name,
|
||||||
})
|
})
|
||||||
@@ -210,16 +210,16 @@ class LDAPController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the uid
|
// Update the uid
|
||||||
if ( req.body.uid !== user.uid ) {
|
if ( req.body.uid.toLowerCase() !== user.uid ) {
|
||||||
// Make sure the UID is free
|
// Make sure the UID is free
|
||||||
const User = this.models.get('auth:User')
|
const User = this.models.get('auth:User')
|
||||||
const existing_user = await User.findOne({ uid: req.body.uid })
|
const existing_user = await User.findOne({ uid: req.body.uid.toLowerCase() })
|
||||||
if ( existing_user )
|
if ( existing_user )
|
||||||
return res.status(400)
|
return res.status(400)
|
||||||
.message(req.T('api.user_already_exists'))
|
.message(req.T('api.user_already_exists'))
|
||||||
.api()
|
.api()
|
||||||
|
|
||||||
user.uid = req.body.uid
|
user.uid = req.body.uid.toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the password
|
// Update the password
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const Oauth2Controller = require('flitter-auth/controllers/Oauth2')
|
|||||||
*/
|
*/
|
||||||
class Oauth2 extends Oauth2Controller {
|
class Oauth2 extends Oauth2Controller {
|
||||||
static get services() {
|
static get services() {
|
||||||
return [...super.services, 'Vue', 'configs', 'models']
|
return [...super.services, 'Vue', 'configs', 'models', 'output']
|
||||||
}
|
}
|
||||||
|
|
||||||
async authorize_post(req, res, next) {
|
async authorize_post(req, res, next) {
|
||||||
@@ -18,6 +18,24 @@ class Oauth2 extends Oauth2Controller {
|
|||||||
const StarshipClient = this.models.get('oauth:Client')
|
const StarshipClient = this.models.get('oauth:Client')
|
||||||
const starship_client = await StarshipClient.findOne({ active: true, uuid: client.clientID })
|
const starship_client = await StarshipClient.findOne({ active: true, uuid: client.clientID })
|
||||||
|
|
||||||
|
// Make sure the user has IAM access before proceeding
|
||||||
|
const Application = this.models.get('Application')
|
||||||
|
const Policy = this.models.get('iam:Policy')
|
||||||
|
const application = await Application.findOne({ oauth_client_ids: starship_client.id })
|
||||||
|
if ( !application ) {
|
||||||
|
this.output.warn('IAM Denial!')
|
||||||
|
return this.Vue.auth_message(res, {
|
||||||
|
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||||
|
next_destination: '/dash',
|
||||||
|
})
|
||||||
|
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||||
|
this.output.warn('IAM Denial!')
|
||||||
|
return this.Vue.auth_message(res, {
|
||||||
|
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||||
|
next_destination: '/dash',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
req.user.authorize(starship_client)
|
req.user.authorize(starship_client)
|
||||||
await req.user.save()
|
await req.user.save()
|
||||||
return super.authorize_post(req, res, next)
|
return super.authorize_post(req, res, next)
|
||||||
@@ -31,6 +49,24 @@ class Oauth2 extends Oauth2Controller {
|
|||||||
const StarshipClient = this.models.get('oauth:Client')
|
const StarshipClient = this.models.get('oauth:Client')
|
||||||
const starship_client = await StarshipClient.findOne({ active: true, uuid: client.clientID })
|
const starship_client = await StarshipClient.findOne({ active: true, uuid: client.clientID })
|
||||||
|
|
||||||
|
// Make sure the user has IAM access before proceeding
|
||||||
|
const Application = this.models.get('Application')
|
||||||
|
const Policy = this.models.get('iam:Policy')
|
||||||
|
const application = await Application.findOne({ oauth_client_ids: starship_client.id })
|
||||||
|
if ( !application ) {
|
||||||
|
this.output.warn('IAM Denial!')
|
||||||
|
return this.Vue.auth_message(res, {
|
||||||
|
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||||
|
next_destination: '/dash',
|
||||||
|
})
|
||||||
|
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||||
|
this.output.warn('IAM Denial!')
|
||||||
|
return this.Vue.auth_message(res, {
|
||||||
|
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||||
|
next_destination: '/dash',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if ( req.user.has_authorized(starship_client) ) {
|
if ( req.user.has_authorized(starship_client) ) {
|
||||||
return this.Vue.invoke_action(res, {
|
return this.Vue.invoke_action(res, {
|
||||||
text: 'Grant Access',
|
text: 'Grant Access',
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class SAMLController extends Controller {
|
|||||||
key: await this.saml.private_key(),
|
key: await this.saml.private_key(),
|
||||||
protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||||
clearIdPSession: done => {
|
clearIdPSession: done => {
|
||||||
this.output.info(`${req.T('saml.clear_idp_session')} ${req.user.uid}`)
|
this.output.info(`${req.T('saml.clear_idp_session')} ${req.user.uid.toLowerCase()}`)
|
||||||
req.saml.participants.clear().then(async () => {
|
req.saml.participants.clear().then(async () => {
|
||||||
if ( this.saml.config().slo.end_coreid_session ) {
|
if ( this.saml.config().slo.end_coreid_session ) {
|
||||||
await req.user.logout(req)
|
await req.user.logout(req)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class LDAPController extends Injectable {
|
|||||||
const item = await this.get_resource_from_dn(req.dn)
|
const item = await this.get_resource_from_dn(req.dn)
|
||||||
if ( !item ) {
|
if ( !item ) {
|
||||||
this.output.debug(`Bind failure: ${req.dn} not found`)
|
this.output.debug(`Bind failure: ${req.dn} not found`)
|
||||||
return next(new LDAP.NoSuchObject())
|
return next(new LDAP.NoSuchObjectError())
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the object is can-able, make sure it can bind
|
// If the object is can-able, make sure it can bind
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class UsersController extends LDAPController {
|
|||||||
first_name: req_data.cn ? req_data.cn[0] : '',
|
first_name: req_data.cn ? req_data.cn[0] : '',
|
||||||
last_name: req_data.sn ? req_data.sn[0] : '',
|
last_name: req_data.sn ? req_data.sn[0] : '',
|
||||||
email: req_data.mail ? req_data.mail[0] : '',
|
email: req_data.mail ? req_data.mail[0] : '',
|
||||||
username: req_data.uid ? req_data.uid[0] : '',
|
username: req_data.uid ? req_data.uid[0].toLowerCase() : '',
|
||||||
password: req_data.userpassword ? req_data.userpassword[0] : '',
|
password: req_data.userpassword ? req_data.userpassword[0] : '',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,6 +299,7 @@ class UsersController extends LDAPController {
|
|||||||
|
|
||||||
// Make sure the user is of appropriate scope
|
// Make sure the user is of appropriate scope
|
||||||
if ( req.dn.equals(user.dn) || req.dn.parentOf(user.dn) ) {
|
if ( req.dn.equals(user.dn) || req.dn.parentOf(user.dn) ) {
|
||||||
|
this.output.debug(await user.to_ldap())
|
||||||
this.output.debug(`Matches sub scope. Matches filter? ${req.filter.matches(await user.to_ldap(iam_targets))}`)
|
this.output.debug(`Matches sub scope. Matches filter? ${req.filter.matches(await user.to_ldap(iam_targets))}`)
|
||||||
|
|
||||||
// Check if filter matches
|
// Check if filter matches
|
||||||
@@ -326,7 +327,7 @@ class UsersController extends LDAPController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if ( typeof dn === 'string' ) dn = LDAP.parseDN(dn)
|
if ( typeof dn === 'string' ) dn = LDAP.parseDN(dn)
|
||||||
return dn.rdns[0].attrs[uid_field].value
|
return dn.rdns[0].attrs[uid_field].value.toLowerCase()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +335,7 @@ class UsersController extends LDAPController {
|
|||||||
const uid = this.get_uid_from_dn(dn)
|
const uid = this.get_uid_from_dn(dn)
|
||||||
if ( uid ) {
|
if ( uid ) {
|
||||||
const User = this.models.get('auth:User')
|
const User = this.models.get('auth:User')
|
||||||
return User.findOne({uid, ldap_visible: true})
|
return User.findOne({uid: uid.toLowerCase(), ldap_visible: true})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
app/models/FrontEndError.model.js
Normal file
29
app/models/FrontEndError.model.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const { Model } = require('flitter-orm')
|
||||||
|
|
||||||
|
class FrontEndErrorModel extends Model {
|
||||||
|
static get schema() {
|
||||||
|
return {
|
||||||
|
user_agent: String,
|
||||||
|
logged_at: { type: Date, default: () => new Date },
|
||||||
|
user_id: String,
|
||||||
|
session_id: String,
|
||||||
|
full_url: String,
|
||||||
|
trace: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async log(request) {
|
||||||
|
const err = new this({
|
||||||
|
user_agent: request.get('user-agent'),
|
||||||
|
user_id: request?.user?.id,
|
||||||
|
session_id: request.sessionID,
|
||||||
|
full_url: request.body.full_url,
|
||||||
|
trace: request.body.trace,
|
||||||
|
})
|
||||||
|
|
||||||
|
await err.save()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = exports = FrontEndErrorModel
|
||||||
@@ -173,7 +173,7 @@ class User extends AuthUser {
|
|||||||
const Policy = this.models.get('iam:Policy')
|
const Policy = this.models.get('iam:Policy')
|
||||||
|
|
||||||
const ldap_data = {
|
const ldap_data = {
|
||||||
uid: this.uid,
|
uid: this.uid.toLowerCase(),
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
cn: this.first_name,
|
cn: this.first_name,
|
||||||
sn: this.last_name,
|
sn: this.last_name,
|
||||||
@@ -213,7 +213,7 @@ class User extends AuthUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get dn() {
|
get dn() {
|
||||||
return LDAP.parseDN(`uid=${this.uid},${this.ldap_server.auth_dn().format(this.configs.get('ldap:server.format'))}`)
|
return LDAP.parseDN(`uid=${this.uid.toLowerCase()},${this.ldap_server.auth_dn().format(this.configs.get('ldap:server.format'))}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following are used by OpenID connect
|
// The following are used by OpenID connect
|
||||||
@@ -227,15 +227,15 @@ class User extends AuthUser {
|
|||||||
given_name: this.first_name,
|
given_name: this.first_name,
|
||||||
locale: 'en_US', // TODO
|
locale: 'en_US', // TODO
|
||||||
name: `${this.first_name} ${this.last_name}`,
|
name: `${this.first_name} ${this.last_name}`,
|
||||||
preferred_username: this.uid,
|
preferred_username: this.uid.toLowerCase(),
|
||||||
username: this.uid,
|
username: this.uid.toLowerCase(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findByLogin(login) {
|
static async findByLogin(login) {
|
||||||
return this.findOne({
|
return this.findOne({
|
||||||
active: true,
|
active: true,
|
||||||
uid: login,
|
uid: login.toLowerCase(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class PolicyModel extends Model {
|
|||||||
if ( this.entity_type === 'user' ) {
|
if ( this.entity_type === 'user' ) {
|
||||||
const User = this.models.get('auth:User')
|
const User = this.models.get('auth:User')
|
||||||
const user = await User.findById(this.entity_id)
|
const user = await User.findById(this.entity_id)
|
||||||
entity_display = `User: ${user.last_name}, ${user.first_name} (${user.uid})`
|
entity_display = `User: ${user.last_name}, ${user.first_name} (${user.uid.toLowerCase()})`
|
||||||
} else if ( this.entity_type === 'group' ) {
|
} else if ( this.entity_type === 'group' ) {
|
||||||
const Group = this.models.get('auth:Group')
|
const Group = this.models.get('auth:Group')
|
||||||
const group = await Group.findById(this.entity_id)
|
const group = await Group.findById(this.entity_id)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class ClientModel extends Model {
|
|||||||
const user = new User({
|
const user = new User({
|
||||||
first_name: name,
|
first_name: name,
|
||||||
last_name: '(LDAP Agent)',
|
last_name: '(LDAP Agent)',
|
||||||
uid,
|
uid: uid.toLowerCase(),
|
||||||
roles: ['ldap_client'],
|
roles: ['ldap_client'],
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ class ClientModel extends Model {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
uid: user.uid,
|
uid: user.uid.toLowerCase(),
|
||||||
last_invocation: this.last_invocation,
|
last_invocation: this.last_invocation,
|
||||||
permissions: [...user.permissions, ...role_permissions],
|
permissions: [...user.permissions, ...role_permissions],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class SessionParticipantStore extends Injectable {
|
|||||||
async issue({ service_provider }) {
|
async issue({ service_provider }) {
|
||||||
const sp = new this.SessionParticipant({
|
const sp = new this.SessionParticipant({
|
||||||
service_provider_id: service_provider.id,
|
service_provider_id: service_provider.id,
|
||||||
name_id: this.request.user.uid,
|
name_id: this.request.user.uid.toLowerCase(),
|
||||||
// session_index: this.get_index(),
|
// session_index: this.get_index(),
|
||||||
slo_url: service_provider.slo_url,
|
slo_url: service_provider.slo_url,
|
||||||
// TODO sp_cert,
|
// TODO sp_cert,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class PermissionMiddleware extends Middleware {
|
|||||||
async test(req, res, next, { check }) {
|
async test(req, res, next, { check }) {
|
||||||
const Policy = this.models.get('iam:Policy')
|
const Policy = this.models.get('iam:Policy')
|
||||||
|
|
||||||
|
if ( !req.additional_api_log_data ) req.additional_api_log_data = {}
|
||||||
req.additional_api_log_data.permission_check = check
|
req.additional_api_log_data.permission_check = check
|
||||||
|
|
||||||
// If the request was authorized using an OAuth2 bearer token,
|
// If the request was authorized using an OAuth2 bearer token,
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ const index = {
|
|||||||
'middleware::auth:GuestOnly',
|
'middleware::auth:GuestOnly',
|
||||||
'controller::api:v1:Password.request_reset',
|
'controller::api:v1:Password.request_reset',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'/api/v1/log-error': [
|
||||||
|
'controller::Home.log_front_end_error'
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class MFAService extends Service {
|
|||||||
secret(user) {
|
secret(user) {
|
||||||
return speakeasy.generateSecret({
|
return speakeasy.generateSecret({
|
||||||
length: this.configs.get('auth.mfa.secret_length') ?? 20,
|
length: this.configs.get('auth.mfa.secret_length') ?? 20,
|
||||||
name: `${this.configs.get('app.name')} (${user.uid})`,
|
name: `${this.configs.get('app.name')} (${user.uid.toLowerCase()})`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class VueService extends Service {
|
|||||||
user: {
|
user: {
|
||||||
first_name: req.user.first_name,
|
first_name: req.user.first_name,
|
||||||
last_name: req.user.last_name,
|
last_name: req.user.last_name,
|
||||||
username: req.user.uid,
|
username: req.user.uid.toLowerCase(),
|
||||||
email: req.user.email,
|
email: req.user.email,
|
||||||
tagline: req.user.tagline,
|
tagline: req.user.tagline,
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class OpenIDConnectUnit extends Unit {
|
|||||||
clients: [],
|
clients: [],
|
||||||
interactions: {
|
interactions: {
|
||||||
interactions,
|
interactions,
|
||||||
url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid}`,
|
url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid.toLowerCase()}`,
|
||||||
},
|
},
|
||||||
cookies: {
|
cookies: {
|
||||||
long: { signed: true, maxAge: 24 * 60 * 60 * 1000 }, // 1 day, ms
|
long: { signed: true, maxAge: 24 * 60 * 60 * 1000 }, // 1 day, ms
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class SettingsUnit extends Unit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async go(app) {
|
async go(app) {
|
||||||
|
Error.stackTraceLimit = 50
|
||||||
app.express.set('trust proxy', true)
|
app.express.set('trust proxy', true)
|
||||||
|
|
||||||
const Setting = this.models.get('Setting')
|
const Setting = this.models.get('Setting')
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ html(lang='en')
|
|||||||
.app-container
|
.app-container
|
||||||
block app
|
block app
|
||||||
block script
|
block script
|
||||||
|
script(src='/assets/error-log.js')
|
||||||
script(src='/assets/lib/axios/axios.min.js')
|
script(src='/assets/lib/axios/axios.min.js')
|
||||||
script(src='/assets/lib/jquery/jquery-3.4.1.slim.min.js')
|
script(src='/assets/lib/jquery/jquery-3.4.1.slim.min.js')
|
||||||
script(src='/assets/lib/popper/popper-1.16.0.min.js')
|
script(src='/assets/lib/popper/popper-1.16.0.min.js')
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"ioredis": "^4.17.1",
|
"ioredis": "^4.17.1",
|
||||||
"is-absolute-url": "^3.0.3",
|
"is-absolute-url": "^3.0.3",
|
||||||
"ldapjs": "^1.0.2",
|
"ldapjs": "^1.0.2",
|
||||||
"libflitter": "^0.56.0",
|
"libflitter": "^0.56.1",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"mongodb": "^3.5.9",
|
"mongodb": "^3.5.9",
|
||||||
"nodemailer": "^6.4.6",
|
"nodemailer": "^6.4.6",
|
||||||
|
|||||||
@@ -3235,10 +3235,10 @@ leven@^1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3"
|
resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3"
|
||||||
integrity sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=
|
integrity sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=
|
||||||
|
|
||||||
libflitter@^0.56.0:
|
libflitter@^0.56.1:
|
||||||
version "0.56.0"
|
version "0.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/libflitter/-/libflitter-0.56.0.tgz#1ff04b7749d55e7a011149b8a6cae561b8e800d8"
|
resolved "https://registry.yarnpkg.com/libflitter/-/libflitter-0.56.1.tgz#250166027b9cab727c9deb6b1fa1865428b1eafb"
|
||||||
integrity sha512-AwCmTZaKPOQqDWiASvxMuWaexBYxuKpY/QMVnrVdW/VXT5eW0rIf+bJy6RdQGiH8GzlB3442+U4yl4VWNfeKsQ==
|
integrity sha512-QikFtFRa9okKOjOio5ehpQ6hyacCoMbtOlqcXt4I7uU3lntBeP5qSbz1q3x1wUY/AdBc2k70+Eg8BcpGmBEs4Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
colors "^1.3.3"
|
colors "^1.3.3"
|
||||||
connect-mongodb-session "^2.2.0"
|
connect-mongodb-session "^2.2.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user