CoreID/app/models/auth/User.model.js

252 lines
7.2 KiB
JavaScript
Raw Normal View History

2020-04-17 00:59:48 +00:00
const AuthUser = require('flitter-auth/model/User')
const LDAP = require('ldapjs')
const ActiveScope = require('../scopes/ActiveScope')
2020-04-22 21:56:39 +00:00
const MFAToken = require('./MFAToken.model')
2020-05-04 01:16:54 +00:00
const PasswordReset = require('./PasswordReset.model')
const AppAuthorization = require('./AppAuthorization.model')
2020-05-04 01:16:54 +00:00
const AppPassword = require('./AppPassword.model')
2020-08-13 06:56:33 +00:00
const uuid = require('uuid').v4
const NotifyConfig = require('../system/NotifyConfig.model')
2020-04-17 00:59:48 +00:00
/*
* Auth user model. This inherits fields and methods from the default
* flitter-auth/model/User model, however you can override methods and
* properties here as you need.
*/
class User extends AuthUser {
2020-04-18 00:25:33 +00:00
static get services() {
2020-05-04 01:16:54 +00:00
return [...super.services, 'auth', 'ldap_server', 'configs', 'models', 'app']
2020-04-18 00:25:33 +00:00
}
2020-04-17 00:59:48 +00:00
static get schema() {
return {...super.schema, ...{
// other schema fields here
first_name: String,
last_name: String,
2020-05-04 01:16:54 +00:00
tagline: String,
email: String,
ldap_visible: {type: Boolean, default: true},
active: {type: Boolean, default: true},
2020-04-22 21:56:39 +00:00
mfa_token: MFAToken,
2020-05-04 01:16:54 +00:00
password_resets: [PasswordReset],
app_passwords: [AppPassword],
app_authorizations: [AppAuthorization],
2020-04-22 21:56:39 +00:00
mfa_enabled: {type: Boolean, default: false},
2020-05-04 01:16:54 +00:00
mfa_enable_date: Date,
create_date: {type: Date, default: () => new Date},
photo_file_id: String,
2020-05-20 14:56:03 +00:00
trap: String,
notify_config: NotifyConfig,
2020-04-17 00:59:48 +00:00
}}
}
async photo() {
const File = this.models.get('upload::File')
return File.findById(this.photo_file_id)
}
has_authorized(client) {
return this.app_authorizations.some(x => x.client_id === client.id)
}
get_authorization(client) {
for ( const auth of this.app_authorizations ) {
if ( auth.client_id === client.id )
return auth
}
}
authorize(client) {
if ( !this.has_authorized(client) ) {
const client_rec = new AppAuthorization({
client_id: client.id,
api_scopes: client.api_scopes,
}, this)
this.app_authorizations.push(client_rec)
} else {
const client_rec = this.get_authorization(client)
client_rec.api_scopes = client.api_scopes
}
}
2020-05-12 01:26:09 +00:00
async to_api() {
return {
id: this.id,
uid: this.uid,
first_name: this.first_name,
last_name: this.last_name,
email: this.email,
tagline: this.tagline,
trap: this.trap,
group_ids: (await this.groups()).map(x => x.id),
2020-05-12 01:26:09 +00:00
}
}
static scopes = [
new ActiveScope({})
]
static async ldap_directory() {
return this.find({ldap_visible: true})
}
// TODO just in case we need this later
get can_login() {
return true
}
2020-05-04 01:16:54 +00:00
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
await this.save()
}
2020-04-18 00:25:33 +00:00
async check_password(password) {
return this.get_provider().check_user_auth(this, password)
}
2020-05-04 01:16:54 +00:00
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 groups() {
const Group = this.models.get('auth:Group')
return Group.find({ active: true, user_ids: this.id })
}
async oidc_sessions() {
const Session = this.models.get('openid:Session')
return Session.find({ 'payload.account': this.id })
}
async logout(request) {
for ( const session of (await this.oidc_sessions()) ) {
await session.delete()
}
this.get_provider().logout(request)
}
async to_ldap(iam_targets = []) {
const Policy = this.models.get('iam:Policy')
const ldap_data = {
2020-10-19 04:27:23 +00:00
uid: this.uid.toLowerCase(),
uuid: this.uuid,
cn: this.first_name,
sn: this.last_name,
gecos: `${this.first_name} ${this.last_name}`,
mail: this.email,
2020-08-20 13:07:47 +00:00
objectClass: ['inetOrgPerson', 'person'],
2020-08-20 14:11:31 +00:00
entryuuid: this.uuid,
entryUUID: this.uuid,
objectGuid: this.uuid,
}
2020-05-04 01:16:54 +00:00
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]}`
}
2020-05-21 01:35:17 +00:00
const groups = await this.groups()
if ( groups.length > 0 ) {
const group_data = groups.map(x => x.dn.format(this.configs.get('ldap:server.format')))
ldap_data.memberOf = group_data
ldap_data.memberof = group_data
2020-05-04 01:16:54 +00:00
}
const iamtarget = []
for ( const target_id of iam_targets ) {
if ( await Policy.check_user_access(this, target_id) ) {
iamtarget.push(target_id)
}
}
ldap_data.iamtarget = iamtarget
return ldap_data
}
get dn() {
2020-10-19 04:27:23 +00:00
return LDAP.parseDN(`uid=${this.uid.toLowerCase()},${this.ldap_server.auth_dn().format(this.configs.get('ldap:server.format'))}`)
2020-04-18 00:25:33 +00:00
}
2020-08-13 06:56:33 +00:00
// The following are used by OpenID connect
async claims(use, scope) {
return {
sub: this.id,
email: this.email,
email_verified: true, // TODO
family_name: this.last_name,
given_name: this.first_name,
locale: 'en_US', // TODO
name: `${this.first_name} ${this.last_name}`,
2020-10-19 04:27:23 +00:00
preferred_username: this.uid.toLowerCase(),
username: this.uid.toLowerCase(),
2020-08-13 06:56:33 +00:00
}
}
static async findByLogin(login) {
return this.findOne({
active: true,
2020-10-19 04:27:23 +00:00
uid: login.toLowerCase(),
2020-08-13 06:56:33 +00:00
})
}
static async findAccount(ctx, id, token) {
return this.findById(id)
}
get accountId() {
return this.id
}
2020-04-17 00:59:48 +00:00
}
module.exports = exports = User