fase(9): auth module with casl rbac and session management

This commit is contained in:
debian
2026-03-05 09:57:49 -05:00
parent 39a5e41f75
commit 7526a5bc15
77 changed files with 3588 additions and 41 deletions

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApiKey = void 0;
const AggregateRoot_1 = require("../../../../shared/domain/AggregateRoot");
const UniqueId_1 = require("../../../../shared/domain/UniqueId");
class ApiKey extends AggregateRoot_1.AggregateRoot {
static create(props, id) {
const keyId = id ?? UniqueId_1.UniqueId.create();
return new ApiKey({
...props,
createdAt: new Date(),
}, keyId);
}
static reconstitute(props, id) {
return new ApiKey(props, id);
}
get userId() { return this.props.userId; }
get orgId() { return this.props.orgId; }
get name() { return this.props.name; }
get keyHash() { return this.props.keyHash; }
get keyPrefix() { return this.props.keyPrefix; }
get permissions() { return this.props.permissions; }
get expiresAt() { return this.props.expiresAt; }
get lastUsedAt() { return this.props.lastUsedAt; }
get createdAt() { return this.props.createdAt; }
isExpired() {
if (!this.props.expiresAt)
return false;
return new Date() > this.props.expiresAt;
}
markUsed() {
this.props.lastUsedAt = new Date();
}
}
exports.ApiKey = ApiKey;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Organization = void 0;
const AggregateRoot_1 = require("../../../../shared/domain/AggregateRoot");
const UniqueId_1 = require("../../../../shared/domain/UniqueId");
const OrgCreated_1 = require("../events/OrgCreated");
class Organization extends AggregateRoot_1.AggregateRoot {
static create(props, id) {
const orgId = id ?? UniqueId_1.UniqueId.create();
const org = new Organization({
...props,
createdAt: new Date(),
}, orgId);
org.addDomainEvent(new OrgCreated_1.OrgCreated(orgId.toString(), {
name: props.name,
slug: props.slug,
}));
return org;
}
static reconstitute(props, id) {
return new Organization(props, id);
}
static slugify(name) {
return name
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
}
get name() { return this.props.name; }
get slug() { return this.props.slug; }
get createdAt() { return this.props.createdAt; }
}
exports.Organization = Organization;

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.User = void 0;
const AggregateRoot_1 = require("../../../../shared/domain/AggregateRoot");
const UniqueId_1 = require("../../../../shared/domain/UniqueId");
const UserCreated_1 = require("../events/UserCreated");
class User extends AggregateRoot_1.AggregateRoot {
static create(props, id) {
const userId = id ?? UniqueId_1.UniqueId.create();
const now = new Date();
const user = new User({
...props,
createdAt: now,
updatedAt: now,
}, userId);
user.addDomainEvent(new UserCreated_1.UserCreated(userId.toString(), {
email: props.email.value,
name: props.name,
role: props.role.value,
}));
return user;
}
static reconstitute(props, id) {
return new User(props, id);
}
get email() { return this.props.email; }
get name() { return this.props.name; }
get passwordHash() { return this.props.passwordHash; }
get role() { return this.props.role; }
get orgId() { return this.props.orgId; }
get createdAt() { return this.props.createdAt; }
get updatedAt() { return this.props.updatedAt; }
assignToOrg(orgId) {
this.props.orgId = orgId;
this.props.updatedAt = new Date();
}
changeRole(role) {
this.props.role = role;
this.props.updatedAt = new Date();
}
}
exports.User = User;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemberInvited = void 0;
const crypto_1 = require("crypto");
class MemberInvited {
constructor(aggregateId, payload) {
this.aggregateId = aggregateId;
this.payload = payload;
this.eventId = (0, crypto_1.randomUUID)();
this.eventName = 'auth.member.invited';
this.occurredOn = new Date();
}
}
exports.MemberInvited = MemberInvited;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OrgCreated = void 0;
const crypto_1 = require("crypto");
class OrgCreated {
constructor(aggregateId, payload) {
this.aggregateId = aggregateId;
this.payload = payload;
this.eventId = (0, crypto_1.randomUUID)();
this.eventName = 'auth.org.created';
this.occurredOn = new Date();
}
}
exports.OrgCreated = OrgCreated;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserCreated = void 0;
const crypto_1 = require("crypto");
class UserCreated {
constructor(aggregateId, payload) {
this.aggregateId = aggregateId;
this.payload = payload;
this.eventId = (0, crypto_1.randomUUID)();
this.eventName = 'auth.user.created';
this.occurredOn = new Date();
}
}
exports.UserCreated = UserCreated;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserLoggedIn = void 0;
const crypto_1 = require("crypto");
class UserLoggedIn {
constructor(aggregateId, payload) {
this.aggregateId = aggregateId;
this.payload = payload;
this.eventId = (0, crypto_1.randomUUID)();
this.eventName = 'auth.user.logged_in';
this.occurredOn = new Date();
}
}
exports.UserLoggedIn = UserLoggedIn;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Email = void 0;
const ValueObject_1 = require("../../../../shared/domain/ValueObject");
class Email extends ValueObject_1.ValueObject {
static create(value) {
const normalized = value.trim().toLowerCase();
if (!Email.EMAIL_REGEX.test(normalized)) {
throw new Error(`Invalid email address: ${value}`);
}
return new Email({ value: normalized });
}
get value() {
return this.props.value;
}
}
exports.Email = Email;
Email.EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Permission = void 0;
const ValueObject_1 = require("../../../../shared/domain/ValueObject");
class Permission extends ValueObject_1.ValueObject {
static create(action, subject) {
return new Permission({ action, subject });
}
get action() { return this.props.action; }
get subject() { return this.props.subject; }
}
exports.Permission = Permission;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Role = void 0;
const ValueObject_1 = require("../../../../shared/domain/ValueObject");
class Role extends ValueObject_1.ValueObject {
static create(value) {
if (!Role.VALID_ROLES.includes(value)) {
throw new Error(`Invalid role: ${value}. Must be one of: ${Role.VALID_ROLES.join(', ')}`);
}
return new Role({ value: value });
}
static owner() { return new Role({ value: 'owner' }); }
static admin() { return new Role({ value: 'admin' }); }
static member() { return new Role({ value: 'member' }); }
static viewer() { return new Role({ value: 'viewer' }); }
get value() {
return this.props.value;
}
isOwner() { return this.props.value === 'owner'; }
isAdmin() { return this.props.value === 'admin'; }
isMember() { return this.props.value === 'member'; }
isViewer() { return this.props.value === 'viewer'; }
}
exports.Role = Role;
Role.OWNER = 'owner';
Role.ADMIN = 'admin';
Role.MEMBER = 'member';
Role.VIEWER = 'viewer';
Role.VALID_ROLES = ['owner', 'admin', 'member', 'viewer'];