All checks were successful
CI / container-job (push) Successful in 41s
- adonisrc.ts: Load official drive_provider and unload custom driver_provider. - packages.json: Add @headlessui/vue dependency for tab components. - AvatarController.ts: Rewrite avatar generation logic to always return the same avatar per user. - auth/UserController.ts: Add profile and profileUpdate methods to support user profile editing. - Submitter/datasetController.ts & app/models/file.ts: Adapt code to use the official drive_provider. - app/models/user.ts: Introduce “isAdmin” getter. - config/drive.ts: Create new configuration for the official drive_provider. - providers/vinejs_provider.ts: Adapt allowedExtensions control to use provided options or database enabled extensions. - resource/js/app.ts: Load default Head and Link components. - resources/js/menu.ts: Add settings-profile.edit menu point. - resources/js/Components/action-message.vue: Add new component for improved user feedback after form submissions. - New avatar-input.vue component: Enable profile picture selection. - Components/CardBox.vue: Alter layout to optionally show HeaderIcon in title bar. - FormControl.vue: Define a readonly prop for textareas. - Improve overall UI with updates to NavBar.vue, UserAvatar.vue, UserAvatarCurrentUser.vue, and add v-model support to password-meter.vue. - Remove profile editing logic from AccountInfo.vue and introduce new profile components (show.vue, update-password-form.vue, update-profile-information.vue). - app.edge: Modify page (add @inertiaHead tag) for better meta management. - routes.ts: Add new routes for editing user profiles. - General npm updates.
204 lines
6.7 KiB
TypeScript
204 lines
6.7 KiB
TypeScript
import { DateTime } from 'luxon';
|
|
import { withAuthFinder } from '@adonisjs/auth/mixins/lucid';
|
|
import { column, manyToMany, hasMany, SnakeCaseNamingStrategy, computed, beforeFetch, beforeFind } 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;
|
|
|
|
@column({})
|
|
public avatar: string;
|
|
|
|
// @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>;
|
|
|
|
@computed({
|
|
serializeAs: 'is_admin',
|
|
})
|
|
public get isAdmin(): boolean {
|
|
const roles = this.roles;
|
|
const isAdmin = roles?.map((role: Role) => role.name).includes('administrator');
|
|
return isAdmin;
|
|
}
|
|
|
|
// public toJSON() {
|
|
// return {
|
|
// ...super.toJSON(),
|
|
// roles: []
|
|
// };
|
|
// }
|
|
@beforeFind()
|
|
@beforeFetch()
|
|
public static preloadRoles(user: User) {
|
|
user.preload('roles')
|
|
}
|
|
|
|
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;
|