[ST-P3-008] feat: Add missing auth entities from erp-core
- tenant.entity.ts: Multi-tenant support - device.entity.ts: User devices tracking - verification-code.entity.ts: MFA verification codes - trusted-device.entity.ts: Trusted device management - mfa-audit-log.entity.ts: MFA audit logging - oauth-provider.entity.ts: OAuth provider configuration - oauth-state.entity.ts: OAuth state management - oauth-user-link.entity.ts: OAuth user linking - user-profile.entity.ts: User profile definitions - profile-tool.entity.ts: Profile tool permissions - profile-module.entity.ts: Profile module access - user-profile-assignment.entity.ts: Profile assignments Total: 12 new entities synchronized from erp-core. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
7f32ee65b6
commit
728f8ae7fd
64
src/modules/auth/entities/device.entity.ts
Normal file
64
src/modules/auth/entities/device.entity.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
} from 'typeorm';
|
||||
import { Tenant } from './tenant.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'devices' })
|
||||
@Index('idx_devices_tenant_id', ['tenantId'])
|
||||
@Index('idx_devices_user_id', ['userId'])
|
||||
@Index('idx_devices_device_id', ['deviceId'])
|
||||
export class Device {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'tenant_id' })
|
||||
tenantId: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false, name: 'device_id' })
|
||||
deviceId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true, name: 'device_name' })
|
||||
deviceName: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, nullable: false, name: 'device_type' })
|
||||
deviceType: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, nullable: true })
|
||||
platform: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, nullable: true, name: 'os_version' })
|
||||
osVersion: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, nullable: true, name: 'app_version' })
|
||||
appVersion: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'push_token' })
|
||||
pushToken: string;
|
||||
|
||||
@Column({ name: 'is_trusted', default: false })
|
||||
isTrusted: boolean;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true, name: 'last_active_at' })
|
||||
lastActiveAt: Date;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamp' })
|
||||
createdAt: Date;
|
||||
|
||||
@ManyToOne(() => Tenant, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'tenant_id' })
|
||||
tenant: Tenant;
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
}
|
||||
@ -1,14 +1,38 @@
|
||||
/**
|
||||
* Auth Entities - Export
|
||||
* Updated: 2026-02-03 (ST-P3-008)
|
||||
*/
|
||||
|
||||
export { RefreshToken } from './refresh-token.entity';
|
||||
// Core entities
|
||||
export { Tenant, TenantStatus } from './tenant.entity';
|
||||
export { User, UserStatus } from './user.entity';
|
||||
export { Role } from './role.entity';
|
||||
export { Permission } from './permission.entity';
|
||||
export { UserRole } from './user-role.entity';
|
||||
export { User, UserStatus } from './user.entity';
|
||||
export { Session, SessionStatus } from './session.entity';
|
||||
export { ApiKey } from './api-key.entity';
|
||||
export { PasswordReset } from './password-reset.entity';
|
||||
export { Company } from './company.entity';
|
||||
export { Group } from './group.entity';
|
||||
|
||||
// Authentication
|
||||
export { RefreshToken } from './refresh-token.entity';
|
||||
export { ApiKey } from './api-key.entity';
|
||||
export { PasswordReset } from './password-reset.entity';
|
||||
export { VerificationCode, CodeType } from './verification-code.entity';
|
||||
|
||||
// Devices
|
||||
export { Device } from './device.entity';
|
||||
export { TrustedDevice, TrustLevel } from './trusted-device.entity';
|
||||
|
||||
// MFA
|
||||
export { MfaAuditLog, MfaEventType } from './mfa-audit-log.entity';
|
||||
|
||||
// OAuth
|
||||
export { OAuthProvider } from './oauth-provider.entity';
|
||||
export { OAuthState } from './oauth-state.entity';
|
||||
export { OAuthUserLink } from './oauth-user-link.entity';
|
||||
|
||||
// User Profiles
|
||||
export { UserProfile } from './user-profile.entity';
|
||||
export { ProfileTool } from './profile-tool.entity';
|
||||
export { ProfileModule } from './profile-module.entity';
|
||||
export { UserProfileAssignment } from './user-profile-assignment.entity';
|
||||
|
||||
80
src/modules/auth/entities/mfa-audit-log.entity.ts
Normal file
80
src/modules/auth/entities/mfa-audit-log.entity.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
|
||||
export enum MfaEventType {
|
||||
MFA_SETUP_INITIATED = 'mfa_setup_initiated',
|
||||
MFA_SETUP_COMPLETED = 'mfa_setup_completed',
|
||||
MFA_DISABLED = 'mfa_disabled',
|
||||
TOTP_VERIFIED = 'totp_verified',
|
||||
TOTP_FAILED = 'totp_failed',
|
||||
BACKUP_CODE_USED = 'backup_code_used',
|
||||
BACKUP_CODES_REGENERATED = 'backup_codes_regenerated',
|
||||
DEVICE_TRUSTED = 'device_trusted',
|
||||
DEVICE_REVOKED = 'device_revoked',
|
||||
ANOMALY_DETECTED = 'anomaly_detected',
|
||||
ACCOUNT_LOCKED = 'account_locked',
|
||||
ACCOUNT_UNLOCKED = 'account_unlocked',
|
||||
}
|
||||
|
||||
@Entity({ schema: 'auth', name: 'mfa_audit_log' })
|
||||
@Index('idx_mfa_audit_user', ['userId', 'createdAt'])
|
||||
@Index('idx_mfa_audit_event', ['eventType', 'createdAt'])
|
||||
@Index('idx_mfa_audit_failures', ['userId', 'createdAt'], {
|
||||
where: 'success = FALSE',
|
||||
})
|
||||
export class MfaAuditLog {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: MfaEventType,
|
||||
nullable: false,
|
||||
name: 'event_type',
|
||||
})
|
||||
eventType: MfaEventType;
|
||||
|
||||
@Column({ type: 'boolean', nullable: false })
|
||||
success: boolean;
|
||||
|
||||
@Column({ type: 'varchar', length: 128, nullable: true, name: 'failure_reason' })
|
||||
failureReason: string | null;
|
||||
|
||||
@Column({ type: 'inet', nullable: true, name: 'ip_address' })
|
||||
ipAddress: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'user_agent' })
|
||||
userAgent: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 128,
|
||||
nullable: true,
|
||||
name: 'device_fingerprint',
|
||||
})
|
||||
deviceFingerprint: string | null;
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
location: Record<string, unknown> | null;
|
||||
|
||||
@Column({ type: 'jsonb', default: {}, nullable: true })
|
||||
metadata: Record<string, unknown>;
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
}
|
||||
181
src/modules/auth/entities/oauth-provider.entity.ts
Normal file
181
src/modules/auth/entities/oauth-provider.entity.ts
Normal file
@ -0,0 +1,181 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { Tenant } from './tenant.entity';
|
||||
import { User } from './user.entity';
|
||||
import { Role } from './role.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'oauth_providers' })
|
||||
@Index('idx_oauth_providers_enabled', ['isEnabled'])
|
||||
@Index('idx_oauth_providers_tenant', ['tenantId'])
|
||||
@Index('idx_oauth_providers_code', ['code'])
|
||||
export class OAuthProvider {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'tenant_id' })
|
||||
tenantId: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, nullable: false, unique: true })
|
||||
code: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 100, nullable: false })
|
||||
name: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false, name: 'client_id' })
|
||||
clientId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 500, nullable: true, name: 'client_secret' })
|
||||
clientSecret: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 500,
|
||||
nullable: false,
|
||||
name: 'authorization_endpoint',
|
||||
})
|
||||
authorizationEndpoint: string;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 500,
|
||||
nullable: false,
|
||||
name: 'token_endpoint',
|
||||
})
|
||||
tokenEndpoint: string;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 500,
|
||||
nullable: false,
|
||||
name: 'userinfo_endpoint',
|
||||
})
|
||||
userinfoEndpoint: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 500, nullable: true, name: 'jwks_uri' })
|
||||
jwksUri: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 500,
|
||||
default: 'openid profile email',
|
||||
nullable: false,
|
||||
})
|
||||
scope: string;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
default: 'code',
|
||||
nullable: false,
|
||||
name: 'response_type',
|
||||
})
|
||||
responseType: string;
|
||||
|
||||
@Column({
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
nullable: false,
|
||||
name: 'pkce_enabled',
|
||||
})
|
||||
pkceEnabled: boolean;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 10,
|
||||
default: 'S256',
|
||||
nullable: true,
|
||||
name: 'code_challenge_method',
|
||||
})
|
||||
codeChallengeMethod: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'jsonb',
|
||||
nullable: false,
|
||||
name: 'claim_mapping',
|
||||
default: {
|
||||
sub: 'oauth_uid',
|
||||
email: 'email',
|
||||
name: 'name',
|
||||
picture: 'avatar_url',
|
||||
},
|
||||
})
|
||||
claimMapping: Record<string, unknown>;
|
||||
|
||||
@Column({ type: 'varchar', length: 100, nullable: true, name: 'icon_class' })
|
||||
iconClass: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 100, nullable: true, name: 'button_text' })
|
||||
buttonText: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, nullable: true, name: 'button_color' })
|
||||
buttonColor: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'integer',
|
||||
default: 10,
|
||||
nullable: false,
|
||||
name: 'display_order',
|
||||
})
|
||||
displayOrder: number;
|
||||
|
||||
@Column({ type: 'boolean', default: false, nullable: false, name: 'is_enabled' })
|
||||
isEnabled: boolean;
|
||||
|
||||
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_visible' })
|
||||
isVisible: boolean;
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
array: true,
|
||||
nullable: true,
|
||||
name: 'allowed_domains',
|
||||
})
|
||||
allowedDomains: string[] | null;
|
||||
|
||||
@Column({
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
nullable: false,
|
||||
name: 'auto_create_users',
|
||||
})
|
||||
autoCreateUsers: boolean;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'default_role_id' })
|
||||
defaultRoleId: string | null;
|
||||
|
||||
@ManyToOne(() => Tenant, { onDelete: 'CASCADE', nullable: true })
|
||||
@JoinColumn({ name: 'tenant_id' })
|
||||
tenant: Tenant | null;
|
||||
|
||||
@ManyToOne(() => Role, { nullable: true })
|
||||
@JoinColumn({ name: 'default_role_id' })
|
||||
defaultRole: Role | null;
|
||||
|
||||
@ManyToOne(() => User, { nullable: true })
|
||||
@JoinColumn({ name: 'created_by' })
|
||||
createdByUser: User | null;
|
||||
|
||||
@ManyToOne(() => User, { nullable: true })
|
||||
@JoinColumn({ name: 'updated_by' })
|
||||
updatedByUser: User | null;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'created_by' })
|
||||
createdBy: string | null;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'updated_by' })
|
||||
updatedBy: string | null;
|
||||
}
|
||||
60
src/modules/auth/entities/oauth-state.entity.ts
Normal file
60
src/modules/auth/entities/oauth-state.entity.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { OAuthProvider } from './oauth-provider.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'oauth_states' })
|
||||
@Index('idx_oauth_states_state', ['state'])
|
||||
@Index('idx_oauth_states_expires', ['expiresAt'])
|
||||
export class OAuthState {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 64, nullable: false, unique: true })
|
||||
state: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 128, nullable: true, name: 'code_verifier' })
|
||||
codeVerifier: string | null;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'provider_id' })
|
||||
providerId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 500, nullable: false, name: 'redirect_uri' })
|
||||
redirectUri: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 500, nullable: true, name: 'return_url' })
|
||||
returnUrl: string | null;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'link_user_id' })
|
||||
linkUserId: string | null;
|
||||
|
||||
@Column({ type: 'inet', nullable: true, name: 'ip_address' })
|
||||
ipAddress: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'user_agent' })
|
||||
userAgent: string | null;
|
||||
|
||||
@ManyToOne(() => OAuthProvider)
|
||||
@JoinColumn({ name: 'provider_id' })
|
||||
provider: OAuthProvider;
|
||||
|
||||
@ManyToOne(() => User, { nullable: true })
|
||||
@JoinColumn({ name: 'link_user_id' })
|
||||
linkUser: User | null;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: false, name: 'expires_at' })
|
||||
expiresAt: Date;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true, name: 'used_at' })
|
||||
usedAt: Date | null;
|
||||
}
|
||||
68
src/modules/auth/entities/oauth-user-link.entity.ts
Normal file
68
src/modules/auth/entities/oauth-user-link.entity.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { OAuthProvider } from './oauth-provider.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'oauth_user_links' })
|
||||
@Index('idx_oauth_links_user', ['userId'])
|
||||
@Index('idx_oauth_links_provider', ['providerId'])
|
||||
@Index('idx_oauth_links_oauth_uid', ['oauthUid'])
|
||||
export class OAuthUserLink {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'provider_id' })
|
||||
providerId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false, name: 'oauth_uid' })
|
||||
oauthUid: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true, name: 'oauth_email' })
|
||||
oauthEmail: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'access_token' })
|
||||
accessToken: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'refresh_token' })
|
||||
refreshToken: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'id_token' })
|
||||
idToken: string | null;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true, name: 'token_expires_at' })
|
||||
tokenExpiresAt: Date | null;
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true, name: 'raw_userinfo' })
|
||||
rawUserinfo: Record<string, unknown> | null;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true, name: 'last_login_at' })
|
||||
lastLoginAt: Date | null;
|
||||
|
||||
@Column({ type: 'integer', default: 0, nullable: false, name: 'login_count' })
|
||||
loginCount: number;
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
|
||||
@ManyToOne(() => OAuthProvider, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'provider_id' })
|
||||
provider: OAuthProvider;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
27
src/modules/auth/entities/profile-module.entity.ts
Normal file
27
src/modules/auth/entities/profile-module.entity.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { UserProfile } from './user-profile.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'profile_modules' })
|
||||
export class ProfileModule {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'profile_id' })
|
||||
profileId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, nullable: false, name: 'module_code' })
|
||||
moduleCode: string;
|
||||
|
||||
@Column({ name: 'is_enabled', default: true })
|
||||
isEnabled: boolean;
|
||||
|
||||
@ManyToOne(() => UserProfile, (p) => p.modules, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'profile_id' })
|
||||
profile: UserProfile;
|
||||
}
|
||||
36
src/modules/auth/entities/profile-tool.entity.ts
Normal file
36
src/modules/auth/entities/profile-tool.entity.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { UserProfile } from './user-profile.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'profile_tools' })
|
||||
export class ProfileTool {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'profile_id' })
|
||||
profileId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, nullable: false, name: 'tool_code' })
|
||||
toolCode: string;
|
||||
|
||||
@Column({ name: 'can_view', default: false })
|
||||
canView: boolean;
|
||||
|
||||
@Column({ name: 'can_create', default: false })
|
||||
canCreate: boolean;
|
||||
|
||||
@Column({ name: 'can_edit', default: false })
|
||||
canEdit: boolean;
|
||||
|
||||
@Column({ name: 'can_delete', default: false })
|
||||
canDelete: boolean;
|
||||
|
||||
@ManyToOne(() => UserProfile, (p) => p.tools, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'profile_id' })
|
||||
profile: UserProfile;
|
||||
}
|
||||
91
src/modules/auth/entities/tenant.entity.ts
Normal file
91
src/modules/auth/entities/tenant.entity.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { Company } from './company.entity';
|
||||
import { User } from './user.entity';
|
||||
import { Role } from './role.entity';
|
||||
|
||||
export enum TenantStatus {
|
||||
ACTIVE = 'active',
|
||||
SUSPENDED = 'suspended',
|
||||
TRIAL = 'trial',
|
||||
CANCELLED = 'cancelled',
|
||||
}
|
||||
|
||||
@Entity({ schema: 'auth', name: 'tenants' })
|
||||
@Index('idx_tenants_subdomain', ['subdomain'])
|
||||
@Index('idx_tenants_status', ['status'], { where: 'deleted_at IS NULL' })
|
||||
@Index('idx_tenants_created_at', ['createdAt'])
|
||||
export class Tenant {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
name: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 100, unique: true, nullable: false })
|
||||
subdomain: string;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 100,
|
||||
unique: true,
|
||||
nullable: false,
|
||||
name: 'schema_name',
|
||||
})
|
||||
schemaName: string;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: TenantStatus,
|
||||
default: TenantStatus.ACTIVE,
|
||||
nullable: false,
|
||||
})
|
||||
status: TenantStatus;
|
||||
|
||||
@Column({ type: 'jsonb', default: {} })
|
||||
settings: Record<string, unknown>;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, default: 'basic', nullable: true })
|
||||
plan: string;
|
||||
|
||||
@Column({ type: 'integer', default: 10, name: 'max_users' })
|
||||
maxUsers: number;
|
||||
|
||||
@OneToMany(() => Company, (company) => company.tenant)
|
||||
companies: Company[];
|
||||
|
||||
@OneToMany(() => User, (user) => user.tenant)
|
||||
users: User[];
|
||||
|
||||
@OneToMany(() => Role, (role) => role.tenant)
|
||||
roles: Role[];
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamp' })
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'created_by' })
|
||||
createdBy: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
name: 'updated_at',
|
||||
type: 'timestamp',
|
||||
nullable: true,
|
||||
})
|
||||
updatedAt: Date | null;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'updated_by' })
|
||||
updatedBy: string | null;
|
||||
|
||||
@Column({ type: 'timestamp', nullable: true, name: 'deleted_at' })
|
||||
deletedAt: Date | null;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'deleted_by' })
|
||||
deletedBy: string | null;
|
||||
}
|
||||
107
src/modules/auth/entities/trusted-device.entity.ts
Normal file
107
src/modules/auth/entities/trusted-device.entity.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
|
||||
export enum TrustLevel {
|
||||
STANDARD = 'standard',
|
||||
HIGH = 'high',
|
||||
TEMPORARY = 'temporary',
|
||||
}
|
||||
|
||||
@Entity({ schema: 'auth', name: 'trusted_devices' })
|
||||
@Index('idx_trusted_devices_user', ['userId'], { where: 'is_active' })
|
||||
@Index('idx_trusted_devices_fingerprint', ['deviceFingerprint'])
|
||||
@Index('idx_trusted_devices_expires', ['trustExpiresAt'], {
|
||||
where: 'trust_expires_at IS NOT NULL AND is_active',
|
||||
})
|
||||
export class TrustedDevice {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 128,
|
||||
nullable: false,
|
||||
name: 'device_fingerprint',
|
||||
})
|
||||
deviceFingerprint: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 128, nullable: true, name: 'device_name' })
|
||||
deviceName: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 32, nullable: true, name: 'device_type' })
|
||||
deviceType: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'user_agent' })
|
||||
userAgent: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 64, nullable: true, name: 'browser_name' })
|
||||
browserName: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 32,
|
||||
nullable: true,
|
||||
name: 'browser_version',
|
||||
})
|
||||
browserVersion: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 64, nullable: true, name: 'os_name' })
|
||||
osName: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 32, nullable: true, name: 'os_version' })
|
||||
osVersion: string | null;
|
||||
|
||||
@Column({ type: 'inet', nullable: false, name: 'registered_ip' })
|
||||
registeredIp: string;
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true, name: 'registered_location' })
|
||||
registeredLocation: Record<string, unknown> | null;
|
||||
|
||||
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_active' })
|
||||
isActive: boolean;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: TrustLevel,
|
||||
default: TrustLevel.STANDARD,
|
||||
nullable: false,
|
||||
name: 'trust_level',
|
||||
})
|
||||
trustLevel: TrustLevel;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true, name: 'trust_expires_at' })
|
||||
trustExpiresAt: Date | null;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: false, name: 'last_used_at' })
|
||||
lastUsedAt: Date;
|
||||
|
||||
@Column({ type: 'inet', nullable: true, name: 'last_used_ip' })
|
||||
lastUsedIp: string | null;
|
||||
|
||||
@Column({ type: 'integer', default: 1, nullable: false, name: 'use_count' })
|
||||
useCount: number;
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true, name: 'revoked_at' })
|
||||
revokedAt: Date | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 128, nullable: true, name: 'revoked_reason' })
|
||||
revokedReason: string | null;
|
||||
}
|
||||
36
src/modules/auth/entities/user-profile-assignment.entity.ts
Normal file
36
src/modules/auth/entities/user-profile-assignment.entity.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { UserProfile } from './user-profile.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'user_profile_assignments' })
|
||||
export class UserProfileAssignment {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'profile_id' })
|
||||
profileId: string;
|
||||
|
||||
@Column({ name: 'is_default', default: false })
|
||||
isDefault: boolean;
|
||||
|
||||
@CreateDateColumn({ name: 'assigned_at', type: 'timestamp' })
|
||||
assignedAt: Date;
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
|
||||
@ManyToOne(() => UserProfile, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'profile_id' })
|
||||
profile: UserProfile;
|
||||
}
|
||||
52
src/modules/auth/entities/user-profile.entity.ts
Normal file
52
src/modules/auth/entities/user-profile.entity.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
} from 'typeorm';
|
||||
import { Tenant } from './tenant.entity';
|
||||
import { ProfileTool } from './profile-tool.entity';
|
||||
import { ProfileModule } from './profile-module.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'user_profiles' })
|
||||
@Index('idx_user_profiles_tenant_id', ['tenantId'])
|
||||
export class UserProfile {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'tenant_id' })
|
||||
tenantId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 10, nullable: false })
|
||||
code: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 100, nullable: false })
|
||||
name: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ name: 'is_active', default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamp' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at', type: 'timestamp', nullable: true })
|
||||
updatedAt: Date;
|
||||
|
||||
@ManyToOne(() => Tenant, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'tenant_id' })
|
||||
tenant: Tenant;
|
||||
|
||||
@OneToMany(() => ProfileTool, (pt) => pt.profile)
|
||||
tools: ProfileTool[];
|
||||
|
||||
@OneToMany(() => ProfileModule, (pm) => pm.profile)
|
||||
modules: ProfileModule[];
|
||||
}
|
||||
82
src/modules/auth/entities/verification-code.entity.ts
Normal file
82
src/modules/auth/entities/verification-code.entity.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { Session } from './session.entity';
|
||||
|
||||
export enum CodeType {
|
||||
TOTP_SETUP = 'totp_setup',
|
||||
SMS = 'sms',
|
||||
EMAIL = 'email',
|
||||
BACKUP = 'backup',
|
||||
}
|
||||
|
||||
@Entity({ schema: 'auth', name: 'verification_codes' })
|
||||
@Index('idx_verification_codes_user', ['userId', 'codeType'], {
|
||||
where: 'used_at IS NULL',
|
||||
})
|
||||
@Index('idx_verification_codes_expires', ['expiresAt'], {
|
||||
where: 'used_at IS NULL',
|
||||
})
|
||||
export class VerificationCode {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'user_id' })
|
||||
userId: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'session_id' })
|
||||
sessionId: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: CodeType,
|
||||
nullable: false,
|
||||
name: 'code_type',
|
||||
})
|
||||
codeType: CodeType;
|
||||
|
||||
@Column({ type: 'varchar', length: 64, nullable: false, name: 'code_hash' })
|
||||
codeHash: string;
|
||||
|
||||
@Column({ type: 'integer', default: 6, nullable: false, name: 'code_length' })
|
||||
codeLength: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 256, nullable: true })
|
||||
destination: string | null;
|
||||
|
||||
@Column({ type: 'integer', default: 0, nullable: false })
|
||||
attempts: number;
|
||||
|
||||
@Column({ type: 'integer', default: 5, nullable: false, name: 'max_attempts' })
|
||||
maxAttempts: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: false, name: 'expires_at' })
|
||||
expiresAt: Date;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true, name: 'used_at' })
|
||||
usedAt: Date | null;
|
||||
|
||||
@Column({ type: 'inet', nullable: true, name: 'ip_address' })
|
||||
ipAddress: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true, name: 'user_agent' })
|
||||
userAgent: string | null;
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
|
||||
@ManyToOne(() => Session, { onDelete: 'CASCADE', nullable: true })
|
||||
@JoinColumn({ name: 'session_id' })
|
||||
session: Session | null;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user