SAML; Dashboard
This commit is contained in:
28
app/models/auth/AppPassword.model.js
Normal file
28
app/models/auth/AppPassword.model.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const { Model } = require('flitter-orm')
|
||||
const bcrypt = require('bcrypt')
|
||||
const uuid = require('uuid/v4')
|
||||
|
||||
class AppPasswordModel extends Model {
|
||||
static get schema() {
|
||||
return {
|
||||
hash: String,
|
||||
created: { type: Date, default: () => new Date },
|
||||
expires: Date,
|
||||
active: { type: Boolean, default: true },
|
||||
name: String,
|
||||
uuid: { type: String, default: uuid },
|
||||
}
|
||||
}
|
||||
|
||||
async set_hash(password) {
|
||||
this.hash = await bcrypt.hash(password, 10)
|
||||
if ( !this.uuid ) this.uuid = uuid()
|
||||
return this
|
||||
}
|
||||
|
||||
async verify(password) {
|
||||
return bcrypt.compare(password, this.hash)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = AppPasswordModel
|
||||
24
app/models/auth/PasswordReset.model.js
Normal file
24
app/models/auth/PasswordReset.model.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const { Model } = require('flitter-orm')
|
||||
const bcrypt = require('bcrypt')
|
||||
|
||||
class PasswordResetModel extends Model {
|
||||
static get schema() {
|
||||
return {
|
||||
reset_on: { type: Date, default: () => new Date },
|
||||
old_hash: String,
|
||||
hash: String,
|
||||
reason: { type: String, default: 'user' }, // 'user' | 'lockout'
|
||||
}
|
||||
}
|
||||
|
||||
async set_hash(password) {
|
||||
this.hash = await bcrypt.hash(password, 10)
|
||||
return this
|
||||
}
|
||||
|
||||
async check(password) {
|
||||
return await bcrypt.compare(password, this.hash)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = PasswordResetModel
|
||||
@@ -3,6 +3,9 @@ const LDAP = require('ldapjs')
|
||||
|
||||
const ActiveScope = require('../scopes/ActiveScope')
|
||||
const MFAToken = require('./MFAToken.model')
|
||||
const PasswordReset = require('./PasswordReset.model')
|
||||
const AppPassword = require('./AppPassword.model')
|
||||
const uuid = require('uuid/v4')
|
||||
|
||||
/*
|
||||
* Auth user model. This inherits fields and methods from the default
|
||||
@@ -11,7 +14,7 @@ const MFAToken = require('./MFAToken.model')
|
||||
*/
|
||||
class User extends AuthUser {
|
||||
static get services() {
|
||||
return [...super.services, 'auth', 'ldap_server', 'ldap_dn_format']
|
||||
return [...super.services, 'auth', 'ldap_server', 'configs', 'models', 'app']
|
||||
}
|
||||
|
||||
static get schema() {
|
||||
@@ -19,11 +22,15 @@ class User extends AuthUser {
|
||||
// other schema fields here
|
||||
first_name: String,
|
||||
last_name: String,
|
||||
tagline: String,
|
||||
email: String,
|
||||
ldap_visible: {type: Boolean, default: true},
|
||||
active: {type: Boolean, default: true},
|
||||
mfa_token: MFAToken,
|
||||
password_resets: [PasswordReset],
|
||||
app_passwords: [AppPassword],
|
||||
mfa_enabled: {type: Boolean, default: false},
|
||||
mfa_enable_date: Date,
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -40,6 +47,22 @@ class User extends AuthUser {
|
||||
return true
|
||||
}
|
||||
|
||||
async sessions() {
|
||||
const Session = require('../Session')
|
||||
this.app.di().inject(Session)
|
||||
return Session.find({ 'session.auth.user_id': this.id })
|
||||
}
|
||||
|
||||
async kickout() {
|
||||
// TODO handle SAML session participants
|
||||
const sessions = await this.sessions()
|
||||
for ( const session of sessions ) {
|
||||
delete session.session.auth
|
||||
delete session.session.mfa_remember
|
||||
await session.save()
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer soft delete because of the active scope
|
||||
async delete() {
|
||||
this.active = false
|
||||
@@ -50,7 +73,45 @@ class User extends AuthUser {
|
||||
return this.get_provider().check_user_auth(this, password)
|
||||
}
|
||||
|
||||
to_ldap() {
|
||||
async check_app_password(password) {
|
||||
for ( const pw of this.app_passwords ) {
|
||||
if ( await pw.verify(password) ) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
async reset_password(new_password, reason = 'user') {
|
||||
const reset = new PasswordReset({
|
||||
reason,
|
||||
old_hash: this.password,
|
||||
}, this)
|
||||
|
||||
await reset.set_hash(new_password)
|
||||
this.password = reset.hash
|
||||
this.password_resets.push(reset)
|
||||
return reset
|
||||
}
|
||||
|
||||
async app_password(name) {
|
||||
const gen = uuid().replace(/-/g, '')
|
||||
const pw = new AppPassword({ name }, this)
|
||||
await pw.set_hash(gen)
|
||||
this.app_passwords.push(pw)
|
||||
return { password: gen, record: pw }
|
||||
}
|
||||
|
||||
async ldap_groups() {
|
||||
const Group = this.models.get('ldap:Group')
|
||||
return await Group.find({
|
||||
$or: [
|
||||
{ user_ids: this.id },
|
||||
{ role: { $in: this.roles } },
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
async to_ldap() {
|
||||
const ldap_data = {
|
||||
uid: this.uid,
|
||||
uuid: this.uuid,
|
||||
@@ -59,20 +120,27 @@ class User extends AuthUser {
|
||||
gecos: `${this.first_name} ${this.last_name}`,
|
||||
mail: this.email,
|
||||
objectClass: 'inetOrgPerson',
|
||||
dn: this.dn.format(this.ldap_dn_format),
|
||||
}
|
||||
|
||||
if ( this.tagline ) ldap_data.extras_tagline = this.tagline
|
||||
|
||||
const addl_data = JSON.parse(this.data)
|
||||
for ( const key in addl_data ) {
|
||||
if ( !addl_data.hasOwnProperty(key) || !key.startsWith('ldap_') ) continue
|
||||
ldap_data[`data${key.substr(4)}`] = `${addl_data[key]}`
|
||||
}
|
||||
|
||||
const ldap_groups = await this.ldap_groups()
|
||||
if ( ldap_groups.length > 0 ) {
|
||||
ldap_data.memberOf = ldap_groups.map(x => x.dn.format(this.configs.get('ldap:server.format')))
|
||||
ldap_data.memberof = ldap_groups.map(x => x.dn.format(this.configs.get('ldap:server.format')))
|
||||
}
|
||||
|
||||
return ldap_data
|
||||
}
|
||||
|
||||
get dn() {
|
||||
return LDAP.parseDN(`uid=${this.uid},${this.ldap_server.auth_dn().format(this.ldap_dn_format)}`)
|
||||
return LDAP.parseDN(`uid=${this.uid},${this.ldap_server.auth_dn().format(this.configs.get('ldap:server.format'))}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user