tethys.backend/app/models/user.ts
Arno Kaimbacher 49bd96ee77 feat: enhance user management, mimetype creation, and validation
- **AdminuserController.ts**: enable editing `first_name` and `last_name` for user creation and updates
- **MimetypeController.ts**: add creation support for mimetypes with selectable extensions
- **Models**: add `Mimetype` model (mime_type.ts); add `SnakeCaseNamingStrategy` for User model
- **Validators**:
  - **updateDatasetValidator**: increase title length to 255 and description length to 2500
  - **User Validators**: refine `createUserValidator` and `updateUserValidator` to include `first_name` and `last_name`
- **vanilla_error_reporter**: improve error reporting for wildcard fields
- **SKOS Query**: refine keyword request in `SearchCategoryAutocomplete.vue`
- **UI Enhancements**:
  - improve icon design in wizard (Wizard.vue)
  - add components for mimetype creation (Create.vue and button in Index.vue)
- **Routes**: update `routes.ts` to include new AdonisJS routes
2024-10-31 11:02:36 +01:00

181 lines
6.2 KiB
TypeScript

import { DateTime } from 'luxon';
import { withAuthFinder } from '@adonisjs/auth/mixins/lucid';
import { column, manyToMany, hasMany, SnakeCaseNamingStrategy } from '@adonisjs/lucid/orm';
import hash from '@adonisjs/core/services/hash';
import Role from './role.js';
import db from '@adonisjs/lucid/services/db';
import config from '@adonisjs/core/services/config';
import Dataset from './dataset.js';
import BaseModel from './base_model.js';
// import Encryption from '@ioc:Adonis/Core/Encryption';
import encryption from '@adonisjs/core/services/encryption';
import { TotpState } from '#contracts/enums';
import type { ManyToMany } from '@adonisjs/lucid/types/relations';
import type { HasMany } from '@adonisjs/lucid/types/relations';
import { compose } from '@adonisjs/core/helpers';
import BackupCode from './backup_code.js';
const AuthFinder = withAuthFinder(() => hash.use('laravel'), {
uids: ['email'],
passwordColumnName: 'password',
});
// import TotpSecret from './TotpSecret';
// export default interface IUser {
// id: number;
// login: string;
// email: string;
// // password: string;
// // createdAt: DateTime;
// // updatedAt: DateTime;
// // async (user): Promise<void>;
// }
// const permissionTable = config.get('rolePermission.permission_table', 'permissions');
// const rolePermissionTable = config.get('rolePermission.role_permission_table', 'role_has_permissions');
// const roleTable = config.get('rolePermission.role_table', 'roles');
// const userRoleTable = config.get('rolePermission.user_role_table', 'link_accounts_roles');
export default class User extends compose(BaseModel, AuthFinder) {
// export default class User extends BaseModel {
public static namingStrategy = new SnakeCaseNamingStrategy();
public static table = 'accounts';
@column({ isPrimary: true })
public id: number;
@column()
public login: string;
@column()
public firstName: string;
@column()
public lastName: string;
@column()
public email: string;
@column({ serializeAs: null })
public password: string;
@column.dateTime({ autoCreate: true })
public createdAt: DateTime;
@column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime;
// serializeAs: null removes the model properties from the serialized output.
@column({
serializeAs: null,
consume: (value: string) => (value ? JSON.parse(encryption.decrypt(value) ?? '{}') : null),
prepare: (value: string) => encryption.encrypt(JSON.stringify(value)),
})
public twoFactorSecret?: string | null;
// serializeAs: null removes the model properties from the serialized output.
@column({
serializeAs: null,
consume: (value: string) => (value ? JSON.parse(encryption.decrypt(value) ?? '[]') : []),
prepare: (value: string[]) => encryption.encrypt(JSON.stringify(value)),
})
public twoFactorRecoveryCodes?: string[] | null;
@column({})
public state: number;
// @hasOne(() => TotpSecret, {
// foreignKey: 'user_id',
// })
// public totp_secret: HasOne<typeof TotpSecret>;
// @beforeSave()
// public static async hashPassword(user: User) {
// if (user.$dirty.password) {
// user.password = await hash.use('laravel').make(user.password);
// }
// }
public get isTwoFactorEnabled(): boolean {
return Boolean(this?.twoFactorSecret && this.state == TotpState.STATE_ENABLED);
// return Boolean(this.totp_secret?.twoFactorSecret);
}
@manyToMany(() => Role, {
pivotForeignKey: 'account_id',
pivotRelatedForeignKey: 'role_id',
pivotTable: 'link_accounts_roles',
})
public roles: ManyToMany<typeof Role>;
@hasMany(() => Dataset, {
foreignKey: 'account_id',
})
public datasets: HasMany<typeof Dataset>;
@hasMany(() => BackupCode, {
foreignKey: 'user_id',
})
public backupcodes: HasMany<typeof BackupCode>;
public async getBackupCodes(this: User): Promise<BackupCode[]> {
const test = await this.related('backupcodes').query();
// return test.map((role) => role.code);
return test;
}
// https://github.com/adonisjs/core/discussions/1872#discussioncomment-132289
public async getRoles(this: User): Promise<string[]> {
const test = await this.related('roles').query();
return test.map((role) => role.name);
}
public async can(permissionNames: Array<string>): Promise<boolean> {
// const permissions = await this.getPermissions()
// return Acl.check(expression, operand => _.includes(permissions, operand))
const hasPermission = await this.checkHasPermissions(this, permissionNames);
return hasPermission;
}
private async checkHasPermissions(user: User, permissionNames: Array<string>): Promise<boolean> {
const permissionTable = config.get('rolePermission.permission_table', 'permissions');
const rolePermissionTable = config.get('rolePermission.role_permission_table', 'role_has_permissions');
const roleTable = config.get('rolePermission.role_table', 'roles');
const userRoleTable = config.get('rolePermission.user_role_table', 'link_accounts_roles');
let permissionPlaceHolder = '(';
let placeholders = new Array(permissionNames.length).fill('?');
permissionPlaceHolder += placeholders.join(',');
permissionPlaceHolder += ')';
let {
rows: {
0: { permissioncount },
},
} = await db.rawQuery(
'SELECT count("p"."name") as permissionCount FROM ' +
roleTable +
' r INNER JOIN ' +
userRoleTable +
' ur ON ur.role_id=r.id AND "ur"."account_id"=? ' +
' INNER JOIN ' +
rolePermissionTable +
' rp ON rp.role_id=r.id ' +
' INNER JOIN ' +
permissionTable +
' p ON rp.permission_id=p.id AND "p"."name" in ' +
permissionPlaceHolder +
' LIMIT 1',
[user.id, ...permissionNames],
);
return permissioncount > 0;
}
}
// export default User;