@knpkv/codecommit-core 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AwsClient/AwsClientGated.d.ts +30 -0
- package/dist/AwsClient/AwsClientGated.d.ts.map +1 -0
- package/dist/AwsClient/AwsClientGated.js +111 -0
- package/dist/AwsClient/AwsClientGated.js.map +1 -0
- package/dist/AwsClient/createApprovalRule.d.ts +4 -0
- package/dist/AwsClient/createApprovalRule.d.ts.map +1 -0
- package/dist/AwsClient/createApprovalRule.js +17 -0
- package/dist/AwsClient/createApprovalRule.js.map +1 -0
- package/dist/AwsClient/deleteApprovalRule.d.ts +4 -0
- package/dist/AwsClient/deleteApprovalRule.d.ts.map +1 -0
- package/dist/AwsClient/deleteApprovalRule.js +16 -0
- package/dist/AwsClient/deleteApprovalRule.js.map +1 -0
- package/dist/AwsClient/getPullRequest.d.ts.map +1 -1
- package/dist/AwsClient/getPullRequest.js +34 -7
- package/dist/AwsClient/getPullRequest.js.map +1 -1
- package/dist/AwsClient/getPullRequests.d.ts +81 -1
- package/dist/AwsClient/getPullRequests.d.ts.map +1 -1
- package/dist/AwsClient/getPullRequests.js +123 -17
- package/dist/AwsClient/getPullRequests.js.map +1 -1
- package/dist/AwsClient/index.d.ts +13 -1
- package/dist/AwsClient/index.d.ts.map +1 -1
- package/dist/AwsClient/index.js +16 -1
- package/dist/AwsClient/index.js.map +1 -1
- package/dist/AwsClient/internal.d.ts +63 -1
- package/dist/AwsClient/internal.d.ts.map +1 -1
- package/dist/AwsClient/internal.js +37 -3
- package/dist/AwsClient/internal.js.map +1 -1
- package/dist/AwsClient/updateApprovalRule.d.ts +4 -0
- package/dist/AwsClient/updateApprovalRule.d.ts.map +1 -0
- package/dist/AwsClient/updateApprovalRule.js +17 -0
- package/dist/AwsClient/updateApprovalRule.js.map +1 -0
- package/dist/CacheService/Database.d.ts +4 -0
- package/dist/CacheService/Database.d.ts.map +1 -1
- package/dist/CacheService/Database.js +15 -1
- package/dist/CacheService/Database.js.map +1 -1
- package/dist/CacheService/EventsHub.d.ts +44 -4
- package/dist/CacheService/EventsHub.d.ts.map +1 -1
- package/dist/CacheService/EventsHub.js.map +1 -1
- package/dist/CacheService/diff.d.ts +17 -1
- package/dist/CacheService/diff.d.ts.map +1 -1
- package/dist/CacheService/diff.js +34 -0
- package/dist/CacheService/diff.js.map +1 -1
- package/dist/CacheService/index.d.ts +5 -1
- package/dist/CacheService/index.d.ts.map +1 -1
- package/dist/CacheService/index.js +5 -1
- package/dist/CacheService/index.js.map +1 -1
- package/dist/CacheService/migrations/0011_audit_log.d.ts +5 -0
- package/dist/CacheService/migrations/0011_audit_log.d.ts.map +1 -0
- package/dist/CacheService/migrations/0011_audit_log.js +19 -0
- package/dist/CacheService/migrations/0011_audit_log.js.map +1 -0
- package/dist/CacheService/migrations/0012_audit_log_indexes.d.ts +5 -0
- package/dist/CacheService/migrations/0012_audit_log_indexes.d.ts.map +1 -0
- package/dist/CacheService/migrations/0012_audit_log_indexes.js +7 -0
- package/dist/CacheService/migrations/0012_audit_log_indexes.js.map +1 -0
- package/dist/CacheService/migrations/0013_approval_rules.d.ts +5 -0
- package/dist/CacheService/migrations/0013_approval_rules.d.ts.map +1 -0
- package/dist/CacheService/migrations/0013_approval_rules.js +4 -0
- package/dist/CacheService/migrations/0013_approval_rules.js.map +1 -0
- package/dist/CacheService/migrations/0014_approved_by_arns.d.ts +5 -0
- package/dist/CacheService/migrations/0014_approved_by_arns.d.ts.map +1 -0
- package/dist/CacheService/migrations/0014_approved_by_arns.js +4 -0
- package/dist/CacheService/migrations/0014_approved_by_arns.js.map +1 -0
- package/dist/CacheService/migrations/0015_repo_account_id.d.ts +5 -0
- package/dist/CacheService/migrations/0015_repo_account_id.d.ts.map +1 -0
- package/dist/CacheService/migrations/0015_repo_account_id.js +4 -0
- package/dist/CacheService/migrations/0015_repo_account_id.js.map +1 -0
- package/dist/CacheService/repos/NotificationRepo.d.ts.map +1 -1
- package/dist/CacheService/repos/NotificationRepo.js +2 -2
- package/dist/CacheService/repos/NotificationRepo.js.map +1 -1
- package/dist/CacheService/repos/PullRequestRepo/index.d.ts +10 -0
- package/dist/CacheService/repos/PullRequestRepo/index.d.ts.map +1 -1
- package/dist/CacheService/repos/PullRequestRepo/internal.d.ts +15 -0
- package/dist/CacheService/repos/PullRequestRepo/internal.d.ts.map +1 -1
- package/dist/CacheService/repos/PullRequestRepo/internal.js +30 -4
- package/dist/CacheService/repos/PullRequestRepo/internal.js.map +1 -1
- package/dist/CacheService/repos/PullRequestRepo/mutations.d.ts +12 -2
- package/dist/CacheService/repos/PullRequestRepo/mutations.d.ts.map +1 -1
- package/dist/CacheService/repos/PullRequestRepo/mutations.js +21 -5
- package/dist/CacheService/repos/PullRequestRepo/mutations.js.map +1 -1
- package/dist/CacheService/repos/PullRequestRepo/queries.d.ts +9 -0
- package/dist/CacheService/repos/PullRequestRepo/queries.d.ts.map +1 -1
- package/dist/CacheService/repos/StatsRepo/internal.d.ts.map +1 -1
- package/dist/CacheService/repos/StatsRepo/internal.js +5 -4
- package/dist/CacheService/repos/StatsRepo/internal.js.map +1 -1
- package/dist/Domain.d.ts +106 -7
- package/dist/Domain.d.ts.map +1 -1
- package/dist/Domain.js +66 -6
- package/dist/Domain.js.map +1 -1
- package/dist/Errors.d.ts +14 -1
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +10 -0
- package/dist/Errors.js.map +1 -1
- package/dist/PRService/internal.d.ts +23 -0
- package/dist/PRService/internal.d.ts.map +1 -1
- package/dist/PRService/internal.js +20 -5
- package/dist/PRService/internal.js.map +1 -1
- package/dist/PRService/refreshFetch.d.ts +5 -1
- package/dist/PRService/refreshFetch.d.ts.map +1 -1
- package/dist/PRService/refreshFetch.js +15 -4
- package/dist/PRService/refreshFetch.js.map +1 -1
- package/dist/PRService/refreshSinglePR.d.ts +7 -0
- package/dist/PRService/refreshSinglePR.d.ts.map +1 -1
- package/dist/PRService/refreshSinglePR.js +16 -3
- package/dist/PRService/refreshSinglePR.js.map +1 -1
- package/dist/PermissionService/AuditLog.d.ts +63 -0
- package/dist/PermissionService/AuditLog.d.ts.map +1 -0
- package/dist/PermissionService/AuditLog.js +127 -0
- package/dist/PermissionService/AuditLog.js.map +1 -0
- package/dist/PermissionService/PermissionGate.d.ts +47 -0
- package/dist/PermissionService/PermissionGate.d.ts.map +1 -0
- package/dist/PermissionService/PermissionGate.js +14 -0
- package/dist/PermissionService/PermissionGate.js.map +1 -0
- package/dist/PermissionService/PermissionGateLive.d.ts +41 -0
- package/dist/PermissionService/PermissionGateLive.d.ts.map +1 -0
- package/dist/PermissionService/PermissionGateLive.js +83 -0
- package/dist/PermissionService/PermissionGateLive.js.map +1 -0
- package/dist/PermissionService/index.d.ts +39 -0
- package/dist/PermissionService/index.d.ts.map +1 -0
- package/dist/PermissionService/index.js +87 -0
- package/dist/PermissionService/index.js.map +1 -0
- package/dist/PermissionService/operations.d.ts +53 -0
- package/dist/PermissionService/operations.d.ts.map +1 -0
- package/dist/PermissionService/operations.js +51 -0
- package/dist/PermissionService/operations.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title AuditLogRepo — records every AWS API call
|
|
3
|
+
*
|
|
4
|
+
* Every call through the permission gate gets recorded — allowed, denied,
|
|
5
|
+
* or timed out.
|
|
6
|
+
*
|
|
7
|
+
* Design constraints:
|
|
8
|
+
* - MUST never block API calls (catchAll on write failures at call site)
|
|
9
|
+
* - MUST handle high volume (~17 ops per refresh × every 5min)
|
|
10
|
+
* - MUST auto-prune (150k entries/month without pruning)
|
|
11
|
+
* - MUST support filtered queries (for the audit log UI page)
|
|
12
|
+
*
|
|
13
|
+
* `durationMs` is null when the API call was denied/timed out (never ran).
|
|
14
|
+
* When present, it reflects actual AWS API latency — not permission prompt wait.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import * as SqlClient from "@effect/sql/SqlClient";
|
|
19
|
+
import { Effect, Schema } from "effect";
|
|
20
|
+
import { CacheError } from "../CacheService/CacheError.js";
|
|
21
|
+
export declare const AuditLogEntry: Schema.Struct<{
|
|
22
|
+
id: typeof Schema.Number;
|
|
23
|
+
timestamp: typeof Schema.String;
|
|
24
|
+
operation: typeof Schema.String;
|
|
25
|
+
accountProfile: typeof Schema.String;
|
|
26
|
+
region: typeof Schema.String;
|
|
27
|
+
permissionState: Schema.Literal<["allowed", "always_allowed", "denied", "timed_out"]>;
|
|
28
|
+
context: typeof Schema.String;
|
|
29
|
+
durationMs: Schema.NullOr<typeof Schema.Number>;
|
|
30
|
+
}>;
|
|
31
|
+
export type AuditLogEntry = typeof AuditLogEntry.Type;
|
|
32
|
+
export type NewAuditLogEntry = Omit<AuditLogEntry, "id">;
|
|
33
|
+
export interface PaginatedAuditLog {
|
|
34
|
+
readonly items: ReadonlyArray<AuditLogEntry>;
|
|
35
|
+
readonly total: number;
|
|
36
|
+
readonly nextCursor?: number;
|
|
37
|
+
}
|
|
38
|
+
declare const AuditLogRepo_base: Effect.Service.Class<AuditLogRepo, "AuditLogRepo", {
|
|
39
|
+
readonly dependencies: readonly [import("effect/Layer").Layer<SqlClient.SqlClient | import("@effect/sql-libsql/LibsqlClient").LibsqlClient, import("@effect/sql/SqlError").SqlError | import("effect/ConfigError").ConfigError | import("@effect/sql/Migrator").MigrationError, import("@effect/platform/FileSystem").FileSystem>];
|
|
40
|
+
readonly effect: Effect.Effect<{
|
|
41
|
+
log: (entry: NewAuditLogEntry) => Effect.Effect<void, CacheError, never>;
|
|
42
|
+
findAll: (opts?: {
|
|
43
|
+
readonly limit?: number | undefined;
|
|
44
|
+
readonly offset?: number | undefined;
|
|
45
|
+
readonly operation?: string | undefined;
|
|
46
|
+
readonly accountProfile?: string | undefined;
|
|
47
|
+
readonly permissionState?: string | undefined;
|
|
48
|
+
readonly from?: string | undefined;
|
|
49
|
+
readonly to?: string | undefined;
|
|
50
|
+
readonly search?: string | undefined;
|
|
51
|
+
}) => Effect.Effect<PaginatedAuditLog, CacheError>;
|
|
52
|
+
prune: (retentionDays: number) => Effect.Effect<number, CacheError>;
|
|
53
|
+
clearAll: () => Effect.Effect<number, CacheError>;
|
|
54
|
+
exportAll: (opts?: {
|
|
55
|
+
readonly from?: string | undefined;
|
|
56
|
+
readonly to?: string | undefined;
|
|
57
|
+
}) => Effect.Effect<ReadonlyArray<AuditLogEntry>, CacheError>;
|
|
58
|
+
}, never, SqlClient.SqlClient>;
|
|
59
|
+
}>;
|
|
60
|
+
export declare class AuditLogRepo extends AuditLogRepo_base {
|
|
61
|
+
}
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=AuditLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuditLog.d.ts","sourceRoot":"","sources":["../../src/PermissionService/AuditLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,SAAS,MAAM,uBAAuB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG1D,eAAO,MAAM,aAAa;;;;;;;;;EASxB,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AAErD,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;AAExD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;;;;qBAmCkB,gBAAgB;yBAQZ;YACf,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YAC5C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SACrC,KAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,UAAU,CAAC;+BAmDzB,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;wBAWnD,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;2BAO5B;YACjB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SACjC,KAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,UAAU,CAAC;;;AApHjE,qBAAa,YAAa,SAAQ,iBAmIhC;CAAG"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title AuditLogRepo — records every AWS API call
|
|
3
|
+
*
|
|
4
|
+
* Every call through the permission gate gets recorded — allowed, denied,
|
|
5
|
+
* or timed out.
|
|
6
|
+
*
|
|
7
|
+
* Design constraints:
|
|
8
|
+
* - MUST never block API calls (catchAll on write failures at call site)
|
|
9
|
+
* - MUST handle high volume (~17 ops per refresh × every 5min)
|
|
10
|
+
* - MUST auto-prune (150k entries/month without pruning)
|
|
11
|
+
* - MUST support filtered queries (for the audit log UI page)
|
|
12
|
+
*
|
|
13
|
+
* `durationMs` is null when the API call was denied/timed out (never ran).
|
|
14
|
+
* When present, it reflects actual AWS API latency — not permission prompt wait.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import * as SqlClient from "@effect/sql/SqlClient";
|
|
19
|
+
import * as SqlSchema from "@effect/sql/SqlSchema";
|
|
20
|
+
import * as Statement from "@effect/sql/Statement";
|
|
21
|
+
import { Effect, Schema } from "effect";
|
|
22
|
+
import { CacheError } from "../CacheService/CacheError.js";
|
|
23
|
+
import { DatabaseLive } from "../CacheService/Database.js";
|
|
24
|
+
export const AuditLogEntry = Schema.Struct({
|
|
25
|
+
id: Schema.Number,
|
|
26
|
+
timestamp: Schema.String,
|
|
27
|
+
operation: Schema.String,
|
|
28
|
+
accountProfile: Schema.String,
|
|
29
|
+
region: Schema.String,
|
|
30
|
+
permissionState: Schema.Literal("allowed", "always_allowed", "denied", "timed_out"),
|
|
31
|
+
context: Schema.String,
|
|
32
|
+
durationMs: Schema.NullOr(Schema.Number)
|
|
33
|
+
});
|
|
34
|
+
const cacheError = (op) => (effect) => effect.pipe(Effect.mapError((cause) => new CacheError({ operation: `AuditLogRepo.${op}`, cause })), Effect.withSpan(`AuditLogRepo.${op}`, { captureStackTrace: false }));
|
|
35
|
+
export class AuditLogRepo extends Effect.Service()("AuditLogRepo", {
|
|
36
|
+
dependencies: [DatabaseLive],
|
|
37
|
+
effect: Effect.gen(function* () {
|
|
38
|
+
const sql = yield* SqlClient.SqlClient;
|
|
39
|
+
const findAll_ = SqlSchema.findAll({
|
|
40
|
+
Result: AuditLogEntry,
|
|
41
|
+
Request: Schema.Struct({
|
|
42
|
+
limit: Schema.Number,
|
|
43
|
+
offset: Schema.Number
|
|
44
|
+
}),
|
|
45
|
+
execute: (req) => sql `SELECT * FROM audit_log ORDER BY id DESC LIMIT ${req.limit} OFFSET ${req.offset}`
|
|
46
|
+
});
|
|
47
|
+
const countAll = SqlSchema.single({
|
|
48
|
+
Result: Schema.Struct({ count: Schema.Number }),
|
|
49
|
+
Request: Schema.Void,
|
|
50
|
+
execute: () => sql `SELECT count(*) as count FROM audit_log`
|
|
51
|
+
});
|
|
52
|
+
const exportAllQuery = SqlSchema.findAll({
|
|
53
|
+
Result: AuditLogEntry,
|
|
54
|
+
Request: Schema.Void,
|
|
55
|
+
execute: () => sql `SELECT * FROM audit_log ORDER BY id DESC`
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
log: (entry) => sql `INSERT INTO audit_log (timestamp, operation, account_profile, region, permission_state, context, duration_ms)
|
|
59
|
+
VALUES (${entry.timestamp}, ${entry.operation}, ${entry.accountProfile}, ${entry.region}, ${entry.permissionState}, ${entry.context}, ${entry.durationMs})`
|
|
60
|
+
.pipe(Effect.asVoid, cacheError("log")),
|
|
61
|
+
findAll: (opts) => {
|
|
62
|
+
// Clamp numeric params
|
|
63
|
+
const limit = Math.max(1, Math.min(200, Math.floor(opts?.limit ?? 50)));
|
|
64
|
+
const offset = Math.max(0, Math.floor(opts?.offset ?? 0));
|
|
65
|
+
// Compose parameterized WHERE using Statement.and
|
|
66
|
+
const hasFilter = opts?.operation || opts?.accountProfile || opts?.permissionState
|
|
67
|
+
|| opts?.from || opts?.to || opts?.search;
|
|
68
|
+
if (hasFilter) {
|
|
69
|
+
const conditions = [];
|
|
70
|
+
if (opts?.operation)
|
|
71
|
+
conditions.push(sql `operation = ${opts.operation}`);
|
|
72
|
+
if (opts?.accountProfile)
|
|
73
|
+
conditions.push(sql `account_profile = ${opts.accountProfile}`);
|
|
74
|
+
if (opts?.permissionState)
|
|
75
|
+
conditions.push(sql `permission_state = ${opts.permissionState}`);
|
|
76
|
+
if (opts?.from)
|
|
77
|
+
conditions.push(sql `timestamp >= ${opts.from}`);
|
|
78
|
+
if (opts?.to)
|
|
79
|
+
conditions.push(sql `timestamp <= ${opts.to}`);
|
|
80
|
+
if (opts?.search) {
|
|
81
|
+
const pattern = `%${opts.search}%`;
|
|
82
|
+
conditions.push(sql `(operation LIKE ${pattern} OR context LIKE ${pattern})`);
|
|
83
|
+
}
|
|
84
|
+
const where = Statement.and(conditions);
|
|
85
|
+
return Effect.all([
|
|
86
|
+
sql `SELECT * FROM audit_log WHERE ${where} ORDER BY id DESC LIMIT ${limit} OFFSET ${offset}`.pipe(Effect.map((rows) => rows.map((r) => Schema.decodeUnknownSync(AuditLogEntry)(r)))),
|
|
87
|
+
sql `SELECT count(*) as count FROM audit_log WHERE ${where}`.pipe(Effect.map((rows) => rows[0]?.count ?? 0))
|
|
88
|
+
]).pipe(Effect.map(([items, total]) => ({
|
|
89
|
+
items,
|
|
90
|
+
total,
|
|
91
|
+
...(items.length === limit ? { nextCursor: offset + limit } : {})
|
|
92
|
+
})), cacheError("findAll"));
|
|
93
|
+
}
|
|
94
|
+
return Effect.all([
|
|
95
|
+
findAll_({ limit, offset }),
|
|
96
|
+
countAll(undefined)
|
|
97
|
+
]).pipe(Effect.map(([items, { count }]) => ({
|
|
98
|
+
items,
|
|
99
|
+
total: count,
|
|
100
|
+
...(items.length === limit ? { nextCursor: offset + limit } : {})
|
|
101
|
+
})), cacheError("findAll"));
|
|
102
|
+
},
|
|
103
|
+
prune: (retentionDays) => {
|
|
104
|
+
const days = Math.max(1, Math.floor(retentionDays));
|
|
105
|
+
const modifier = `-${days} days`;
|
|
106
|
+
return sql `DELETE FROM audit_log WHERE timestamp < datetime('now', ${modifier})`.pipe(
|
|
107
|
+
// DELETE returns no rows — use changes() to get actual deleted count
|
|
108
|
+
Effect.flatMap(() => sql `SELECT changes() as n`), Effect.map((rows) => rows[0]?.n ?? 0), cacheError("prune"));
|
|
109
|
+
},
|
|
110
|
+
clearAll: () => sql `DELETE FROM audit_log`.pipe(Effect.flatMap(() => sql `SELECT changes() as n`), Effect.map((rows) => rows[0]?.n ?? 0), cacheError("clearAll")),
|
|
111
|
+
exportAll: (opts) => {
|
|
112
|
+
if (opts?.from && opts?.to) {
|
|
113
|
+
const exportFiltered = SqlSchema.findAll({
|
|
114
|
+
Result: AuditLogEntry,
|
|
115
|
+
Request: Schema.Void,
|
|
116
|
+
execute: () => sql `SELECT * FROM audit_log WHERE timestamp >= ${opts.from} AND timestamp <= ${opts
|
|
117
|
+
.to} ORDER BY id DESC`
|
|
118
|
+
});
|
|
119
|
+
return exportFiltered(undefined).pipe(cacheError("exportAll"));
|
|
120
|
+
}
|
|
121
|
+
return exportAllQuery(undefined).pipe(cacheError("exportAll"));
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
})
|
|
125
|
+
}) {
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=AuditLog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuditLog.js","sourceRoot":"","sources":["../../src/PermissionService/AuditLog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title PermissionGate — abstract "ask the user" interface
|
|
3
|
+
*
|
|
4
|
+
* Context.Tag (not Effect.Service) so different environments can
|
|
5
|
+
* provide different implementations:
|
|
6
|
+
* - Web: Deferred + SSE push (PermissionGateLive.ts)
|
|
7
|
+
* - TUI: stdin prompt (future)
|
|
8
|
+
* - Test: auto-allow Layer
|
|
9
|
+
*
|
|
10
|
+
* Only exposes `request` — the caller's view. The concrete implementation
|
|
11
|
+
* (PermissionGateLive) adds `resolve` and `getFirstPending` for the
|
|
12
|
+
* HTTP handler and SSE builder.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import type { Effect } from "effect";
|
|
17
|
+
import { Context, Schema } from "effect";
|
|
18
|
+
import type { PermissionDeniedError } from "../Errors.js";
|
|
19
|
+
declare const PermissionPrompt_base: Schema.Class<PermissionPrompt, {
|
|
20
|
+
id: typeof Schema.String;
|
|
21
|
+
operation: typeof Schema.String;
|
|
22
|
+
category: Schema.Literal<["read", "write"]>;
|
|
23
|
+
context: typeof Schema.String;
|
|
24
|
+
}, Schema.Struct.Encoded<{
|
|
25
|
+
id: typeof Schema.String;
|
|
26
|
+
operation: typeof Schema.String;
|
|
27
|
+
category: Schema.Literal<["read", "write"]>;
|
|
28
|
+
context: typeof Schema.String;
|
|
29
|
+
}>, never, {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
} & {
|
|
32
|
+
readonly operation: string;
|
|
33
|
+
} & {
|
|
34
|
+
readonly context: string;
|
|
35
|
+
} & {
|
|
36
|
+
readonly category: "read" | "write";
|
|
37
|
+
}, {}, {}>;
|
|
38
|
+
export declare class PermissionPrompt extends PermissionPrompt_base {
|
|
39
|
+
}
|
|
40
|
+
export type PermissionResponse = "allow_once" | "always_allow" | "deny";
|
|
41
|
+
declare const PermissionGate_base: Context.TagClass<PermissionGate, "@knpkv/codecommit-core/PermissionGate", {
|
|
42
|
+
readonly request: (prompt: PermissionPrompt) => Effect.Effect<PermissionResponse, PermissionDeniedError>;
|
|
43
|
+
}>;
|
|
44
|
+
export declare class PermissionGate extends PermissionGate_base {
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=PermissionGate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionGate.d.ts","sourceRoot":"","sources":["../../src/PermissionService/PermissionGate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;;;;;;;;;;;;;;;;;;;;AAGzD,qBAAa,gBAAiB,SAAQ,qBAKpC;CAAG;AAEL,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,CAAA;;sBAOjD,CAAC,MAAM,EAAE,gBAAgB,KAAK,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC;;AAH5G,qBAAa,cAAe,SAAQ,mBAKjC;CAAG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Context, Schema } from "effect";
|
|
2
|
+
// UUID correlates the SSE prompt → client modal → POST response
|
|
3
|
+
export class PermissionPrompt extends Schema.Class("PermissionPrompt")({
|
|
4
|
+
id: Schema.String,
|
|
5
|
+
operation: Schema.String,
|
|
6
|
+
category: Schema.Literal("read", "write"),
|
|
7
|
+
context: Schema.String
|
|
8
|
+
}) {
|
|
9
|
+
}
|
|
10
|
+
// Blocks the calling fiber until user responds or 30s timeout.
|
|
11
|
+
// Returns the response, or fails with PermissionDeniedError.
|
|
12
|
+
export class PermissionGate extends Context.Tag("@knpkv/codecommit-core/PermissionGate")() {
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=PermissionGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionGate.js","sourceRoot":"","sources":["../../src/PermissionService/PermissionGate.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAGxC,gEAAgE;AAChE,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,KAAK,CAAmB,kBAAkB,CAAC,CAAC;IACvF,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,SAAS,EAAE,MAAM,CAAC,MAAM;IACxB,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CAAC;CAAG;AAIL,+DAA+D;AAC/D,6DAA6D;AAC7D,MAAM,OAAO,cAAe,SAAQ,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,EAKrF;CAAG"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title PermissionGateLive — Deferred-based prompt flow
|
|
3
|
+
*
|
|
4
|
+
* One `Deferred<PermissionResponse>` per prompt.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. API call fiber creates Deferred, stores in pending Map
|
|
8
|
+
* 2. Publishes PermissionRequired → SSE rebuilds payload with prompt
|
|
9
|
+
* 3. Fiber blocks on Deferred.await (up to 30s)
|
|
10
|
+
* 4. User clicks modal → POST /api/permissions/respond
|
|
11
|
+
* 5. HTTP handler calls resolve() → Deferred.succeed → fiber unblocks
|
|
12
|
+
* 6. Publishes PermissionResolved → SSE removes prompt from payload
|
|
13
|
+
*
|
|
14
|
+
* Multi-tab: Deferred.succeed is idempotent — first responder wins,
|
|
15
|
+
* second tab's POST is a no-op. Both see prompt disappear via SSE.
|
|
16
|
+
*
|
|
17
|
+
* Separate Effect.Service tag (not just PermissionGate) because the
|
|
18
|
+
* HTTP handler and SSE builder need `resolve` and `getFirstPending`
|
|
19
|
+
* which the abstract interface doesn't expose.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import { Effect, Layer } from "effect";
|
|
24
|
+
import { EventsHub } from "../CacheService/EventsHub.js";
|
|
25
|
+
import { PermissionDeniedError } from "../Errors.js";
|
|
26
|
+
import { PermissionGate, type PermissionPrompt, type PermissionResponse } from "./PermissionGate.js";
|
|
27
|
+
export interface PermissionGateLive {
|
|
28
|
+
readonly request: (prompt: PermissionPrompt) => Effect.Effect<PermissionResponse, PermissionDeniedError>;
|
|
29
|
+
readonly resolve: (promptId: string, response: PermissionResponse) => Effect.Effect<void>;
|
|
30
|
+
readonly getFirstPending: () => Effect.Effect<PermissionPrompt | undefined>;
|
|
31
|
+
}
|
|
32
|
+
export declare const PermissionGateLiveTag: Effect.Service.Class<PermissionGateLive, "PermissionGateLive", {
|
|
33
|
+
readonly dependencies: readonly [Layer.Layer<EventsHub, never, never>];
|
|
34
|
+
readonly effect: Effect.Effect<{
|
|
35
|
+
request: (prompt: PermissionPrompt) => Effect.Effect<PermissionResponse, PermissionDeniedError>;
|
|
36
|
+
resolve: (promptId: string, response: PermissionResponse) => Effect.Effect<void>;
|
|
37
|
+
getFirstPending: () => Effect.Effect<PermissionPrompt | undefined>;
|
|
38
|
+
}, never, EventsHub>;
|
|
39
|
+
}>;
|
|
40
|
+
export declare const PermissionGateLiveLayer: Layer.Layer<PermissionGate, never, PermissionGateLive>;
|
|
41
|
+
//# sourceMappingURL=PermissionGateLive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionGateLive.d.ts","sourceRoot":"","sources":["../../src/PermissionService/PermissionGateLive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAY,MAAM,EAAE,KAAK,EAAO,MAAM,QAAQ,CAAA;AACrD,OAAO,EAAE,SAAS,EAAc,MAAM,8BAA8B,CAAA;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAOpG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAA;IACxG,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACzF,QAAQ,CAAC,eAAe,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAA;CAC5E;AAED,eAAO,MAAM,qBAAqB;;;0BAWL,gBAAgB,KAAG,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC;4BAqCzE,MAAM,YAAY,kBAAkB,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;+BAUzD,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC;;EAUzE,CAAA;AAKF,eAAO,MAAM,uBAAuB,wDAGnC,CAAA"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title PermissionGateLive — Deferred-based prompt flow
|
|
3
|
+
*
|
|
4
|
+
* One `Deferred<PermissionResponse>` per prompt.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. API call fiber creates Deferred, stores in pending Map
|
|
8
|
+
* 2. Publishes PermissionRequired → SSE rebuilds payload with prompt
|
|
9
|
+
* 3. Fiber blocks on Deferred.await (up to 30s)
|
|
10
|
+
* 4. User clicks modal → POST /api/permissions/respond
|
|
11
|
+
* 5. HTTP handler calls resolve() → Deferred.succeed → fiber unblocks
|
|
12
|
+
* 6. Publishes PermissionResolved → SSE removes prompt from payload
|
|
13
|
+
*
|
|
14
|
+
* Multi-tab: Deferred.succeed is idempotent — first responder wins,
|
|
15
|
+
* second tab's POST is a no-op. Both see prompt disappear via SSE.
|
|
16
|
+
*
|
|
17
|
+
* Separate Effect.Service tag (not just PermissionGate) because the
|
|
18
|
+
* HTTP handler and SSE builder need `resolve` and `getFirstPending`
|
|
19
|
+
* which the abstract interface doesn't expose.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import { Deferred, Effect, Layer, Ref } from "effect";
|
|
24
|
+
import { EventsHub, RepoChange } from "../CacheService/EventsHub.js";
|
|
25
|
+
import { PermissionDeniedError } from "../Errors.js";
|
|
26
|
+
import { PermissionGate } from "./PermissionGate.js";
|
|
27
|
+
export const PermissionGateLiveTag = Effect.Service()("PermissionGateLive", {
|
|
28
|
+
// EventsHub needed to publish PermissionRequired/Resolved events
|
|
29
|
+
// that trigger SSE payload rebuilds
|
|
30
|
+
dependencies: [EventsHub.Default],
|
|
31
|
+
effect: Effect.gen(function* () {
|
|
32
|
+
const hub = yield* EventsHub;
|
|
33
|
+
// Map<promptId, { deferred, prompt }>. Concurrent-safe via Ref.
|
|
34
|
+
// Multiple prompts can be pending simultaneously (e.g. initial
|
|
35
|
+
// refresh triggers getCallerIdentity + listRepositories at once).
|
|
36
|
+
const pending = yield* Ref.make(new Map());
|
|
37
|
+
const request = (prompt) => Effect.gen(function* () {
|
|
38
|
+
const deferred = yield* Deferred.make();
|
|
39
|
+
yield* Ref.update(pending, (m) => new Map(m).set(prompt.id, { deferred, prompt }));
|
|
40
|
+
yield* hub.publish(RepoChange.PermissionRequired());
|
|
41
|
+
const response = yield* Deferred.await(deferred).pipe(Effect.timeout("30 seconds"), Effect.catchTag("TimeoutException", () => {
|
|
42
|
+
return Effect.gen(function* () {
|
|
43
|
+
yield* Ref.update(pending, (m) => {
|
|
44
|
+
const n = new Map(m);
|
|
45
|
+
n.delete(prompt.id);
|
|
46
|
+
return n;
|
|
47
|
+
});
|
|
48
|
+
yield* hub.publish(RepoChange.PermissionResolved());
|
|
49
|
+
return yield* new PermissionDeniedError({ operation: prompt.operation, reason: "timeout" });
|
|
50
|
+
});
|
|
51
|
+
}));
|
|
52
|
+
yield* Ref.update(pending, (m) => {
|
|
53
|
+
const n = new Map(m);
|
|
54
|
+
n.delete(prompt.id);
|
|
55
|
+
return n;
|
|
56
|
+
});
|
|
57
|
+
yield* hub.publish(RepoChange.PermissionResolved());
|
|
58
|
+
if (response === "deny") {
|
|
59
|
+
return yield* new PermissionDeniedError({ operation: prompt.operation, reason: "denied" });
|
|
60
|
+
}
|
|
61
|
+
return response;
|
|
62
|
+
});
|
|
63
|
+
// Called by POST /api/permissions/respond handler.
|
|
64
|
+
// Deferred.succeed is idempotent — second call is a no-op.
|
|
65
|
+
// This is how multi-tab "first responder wins" works.
|
|
66
|
+
const resolve = (promptId, response) => Ref.get(pending).pipe(Effect.flatMap((m) => {
|
|
67
|
+
const entry = m.get(promptId);
|
|
68
|
+
return entry ? Deferred.succeed(entry.deferred, response) : Effect.void;
|
|
69
|
+
}));
|
|
70
|
+
// For SSE payload builder — shows one prompt at a time (FIFO).
|
|
71
|
+
// Remaining prompts queue behind; they'll surface as each resolves.
|
|
72
|
+
const getFirstPending = () => Ref.get(pending).pipe(Effect.map((m) => {
|
|
73
|
+
const first = m.values().next();
|
|
74
|
+
return first.done ? undefined : first.value.prompt;
|
|
75
|
+
}));
|
|
76
|
+
return { request, resolve, getFirstPending };
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
// Bridge: concrete service → abstract Context.Tag.
|
|
80
|
+
// Handlers use PermissionGateLiveTag (for resolve/getFirstPending),
|
|
81
|
+
// AwsClientGated uses PermissionGate (for request only).
|
|
82
|
+
export const PermissionGateLiveLayer = Layer.effect(PermissionGate, Effect.map(PermissionGateLiveTag, (live) => ({ request: live.request })));
|
|
83
|
+
//# sourceMappingURL=PermissionGateLive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionGateLive.js","sourceRoot":"","sources":["../../src/PermissionService/PermissionGateLive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title PermissionService — permission state management
|
|
3
|
+
*
|
|
4
|
+
* Ref<Config> backed by `~/.codecommit/permissions.json`.
|
|
5
|
+
*
|
|
6
|
+
* On construction (Layer build time), reads the file
|
|
7
|
+
* into a Ref. All subsequent `check()` calls are O(1) Ref lookups — no disk
|
|
8
|
+
* I/O. Only `set()` mutates the Ref AND writes to disk (atomic: tmp → rename).
|
|
9
|
+
*
|
|
10
|
+
* Key invariant: an operation missing from the file defaults to `"allow"`,
|
|
11
|
+
* which means "prompt the user". A fresh install with empty permissions.json
|
|
12
|
+
* prompts for every API call — zero-trust by default.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import { FileSystem } from "@effect/platform";
|
|
17
|
+
import { Effect, Schema } from "effect";
|
|
18
|
+
import { allOperations, getOperationMeta, type OperationName, registerOperation } from "./operations.js";
|
|
19
|
+
export type { BuiltinOperation, OperationMeta, OperationName } from "./operations.js";
|
|
20
|
+
export { allOperations, getOperationMeta, registerOperation };
|
|
21
|
+
export declare const PermissionState: Schema.Literal<["always_allow", "allow", "deny"]>;
|
|
22
|
+
export type PermissionState = typeof PermissionState.Type;
|
|
23
|
+
declare const PermissionService_base: Effect.Service.Class<PermissionService, "PermissionService", {
|
|
24
|
+
readonly effect: Effect.Effect<{
|
|
25
|
+
check: (operation: OperationName) => Effect.Effect<PermissionState>;
|
|
26
|
+
set: (operation: OperationName, state: PermissionState) => Effect.Effect<void>;
|
|
27
|
+
getAll: () => Effect.Effect<Record<string, PermissionState>>;
|
|
28
|
+
resetAll: () => Effect.Effect<void>;
|
|
29
|
+
isAuditEnabled: () => Effect.Effect<boolean>;
|
|
30
|
+
getAuditRetention: () => Effect.Effect<number>;
|
|
31
|
+
setAudit: (opts: {
|
|
32
|
+
enabled?: boolean | undefined;
|
|
33
|
+
retentionDays?: number | undefined;
|
|
34
|
+
}) => Effect.Effect<void>;
|
|
35
|
+
}, never, FileSystem.FileSystem>;
|
|
36
|
+
}>;
|
|
37
|
+
export declare class PermissionService extends PermissionService_base {
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/PermissionService/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAU,MAAM,EAAO,MAAM,EAAE,MAAM,QAAQ,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExG,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACrF,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAA;AAK7D,eAAO,MAAM,eAAe,mDAAkD,CAAA;AAC9E,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAA;;;2BA6D3B,aAAa,KAAG,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC;yBAGhD,aAAa,SAAS,eAAe,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;sBAShE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;wBAG5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;8BAMb,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;iCAEnB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;yBAI3C;YAAE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SAAE,KAC1E,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;;;AAzC1B,qBAAa,iBAAkB,SAAQ,sBAuDrC;CAAG"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title PermissionService — permission state management
|
|
3
|
+
*
|
|
4
|
+
* Ref<Config> backed by `~/.codecommit/permissions.json`.
|
|
5
|
+
*
|
|
6
|
+
* On construction (Layer build time), reads the file
|
|
7
|
+
* into a Ref. All subsequent `check()` calls are O(1) Ref lookups — no disk
|
|
8
|
+
* I/O. Only `set()` mutates the Ref AND writes to disk (atomic: tmp → rename).
|
|
9
|
+
*
|
|
10
|
+
* Key invariant: an operation missing from the file defaults to `"allow"`,
|
|
11
|
+
* which means "prompt the user". A fresh install with empty permissions.json
|
|
12
|
+
* prompts for every API call — zero-trust by default.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import { FileSystem } from "@effect/platform";
|
|
17
|
+
import { Config, Effect, Ref, Schema } from "effect";
|
|
18
|
+
import { allOperations, getOperationMeta, registerOperation } from "./operations.js";
|
|
19
|
+
export { allOperations, getOperationMeta, registerOperation };
|
|
20
|
+
// On-disk format. Schema.decodeUnknownSync with defaults means
|
|
21
|
+
// corrupt or missing files gracefully degrade to empty state (everything prompts).
|
|
22
|
+
export const PermissionState = Schema.Literal("always_allow", "allow", "deny");
|
|
23
|
+
const AuditConfig = Schema.Struct({
|
|
24
|
+
enabled: Schema.Boolean.pipe(Schema.optionalWith({ default: () => false })),
|
|
25
|
+
retentionDays: Schema.Number.pipe(Schema.optionalWith({ default: () => 30 }))
|
|
26
|
+
});
|
|
27
|
+
const PermissionsConfig = Schema.Struct({
|
|
28
|
+
permissions: Schema.Record({ key: Schema.String, value: PermissionState }).pipe(Schema.optionalWith({ default: () => ({}) })),
|
|
29
|
+
audit: AuditConfig.pipe(Schema.optionalWith({ default: () => Schema.decodeSync(AuditConfig)({}) }))
|
|
30
|
+
});
|
|
31
|
+
const decodeConfig = Schema.decodeUnknownSync(PermissionsConfig);
|
|
32
|
+
// Uses @effect/platform FileSystem — works in Bun, Node, tests.
|
|
33
|
+
// Atomic write: write to .tmp, then rename. Prevents corruption on crash.
|
|
34
|
+
const resolvePermissionsPath = Config.string("HOME").pipe(Config.orElse(() => Config.string("USERPROFILE")), Config.map((h) => `${h}/.codecommit/permissions.json`));
|
|
35
|
+
const loadFromDisk = (fs, path) => fs.readFileString(path).pipe(Effect.map((content) => decodeConfig(JSON.parse(content))),
|
|
36
|
+
// Any failure → empty config → everything prompts
|
|
37
|
+
Effect.catchAll(() => Effect.succeed(decodeConfig({}))));
|
|
38
|
+
const saveToDisk = (fs, path, config) => Effect.gen(function* () {
|
|
39
|
+
const dir = path.replace(/\/[^/]+$/, "");
|
|
40
|
+
yield* fs.makeDirectory(dir, { recursive: true }).pipe(Effect.catchAll(() => Effect.void));
|
|
41
|
+
const tmpPath = `${path}.tmp`;
|
|
42
|
+
yield* fs.writeFileString(tmpPath, JSON.stringify(config, null, 2));
|
|
43
|
+
yield* fs.rename(tmpPath, path);
|
|
44
|
+
}).pipe(Effect.catchAll(() => Effect.void));
|
|
45
|
+
// Effect.Service (not Context.Tag) — auto-generates `.Default` layer.
|
|
46
|
+
// The effect block runs once at Layer construction: reads file, creates Ref.
|
|
47
|
+
// FileSystem comes from the providing layer (PlatformLive in Server.ts).
|
|
48
|
+
export class PermissionService extends Effect.Service()("PermissionService", {
|
|
49
|
+
effect: Effect.gen(function* () {
|
|
50
|
+
const fs = yield* FileSystem.FileSystem;
|
|
51
|
+
const permPath = yield* Effect.configProviderWith((p) => p.load(resolvePermissionsPath)).pipe(Effect.catchAll(() => Effect.succeed("/tmp/.codecommit/permissions.json")));
|
|
52
|
+
// In-memory state. All check() calls read from here.
|
|
53
|
+
// Only set() mutates it AND writes to disk.
|
|
54
|
+
const initial = yield* loadFromDisk(fs, permPath);
|
|
55
|
+
const configRef = yield* Ref.make(initial);
|
|
56
|
+
// O(1) — Ref.get + property lookup. The "allow" default is the key invariant:
|
|
57
|
+
// missing operation → prompt the user.
|
|
58
|
+
const check = (operation) => Ref.get(configRef).pipe(Effect.map((c) => c.permissions[operation] ?? "allow"));
|
|
59
|
+
const set = (operation, state) => Effect.gen(function* () {
|
|
60
|
+
yield* Ref.update(configRef, (c) => ({
|
|
61
|
+
...c,
|
|
62
|
+
permissions: { ...c.permissions, [operation]: state }
|
|
63
|
+
}));
|
|
64
|
+
yield* saveToDisk(fs, permPath, yield* Ref.get(configRef));
|
|
65
|
+
});
|
|
66
|
+
const getAll = () => Ref.get(configRef).pipe(Effect.map((c) => c.permissions));
|
|
67
|
+
const resetAll = () => Effect.gen(function* () {
|
|
68
|
+
yield* Ref.update(configRef, (c) => ({ ...c, permissions: {} }));
|
|
69
|
+
yield* saveToDisk(fs, permPath, yield* Ref.get(configRef));
|
|
70
|
+
});
|
|
71
|
+
const isAuditEnabled = () => Ref.get(configRef).pipe(Effect.map((c) => c.audit.enabled));
|
|
72
|
+
const getAuditRetention = () => Ref.get(configRef).pipe(Effect.map((c) => c.audit.retentionDays));
|
|
73
|
+
const setAudit = (opts) => Effect.gen(function* () {
|
|
74
|
+
yield* Ref.update(configRef, (c) => ({
|
|
75
|
+
...c,
|
|
76
|
+
audit: {
|
|
77
|
+
enabled: opts.enabled ?? c.audit.enabled,
|
|
78
|
+
retentionDays: opts.retentionDays ?? c.audit.retentionDays
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
yield* saveToDisk(fs, permPath, yield* Ref.get(configRef));
|
|
82
|
+
});
|
|
83
|
+
return { check, set, getAll, resetAll, isAuditEnabled, getAuditRetention, setAudit };
|
|
84
|
+
})
|
|
85
|
+
}) {
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/PermissionService/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Operation registry — maps operation names to metadata
|
|
3
|
+
*
|
|
4
|
+
* Every AWS API call has a named operation with a category (read/write)
|
|
5
|
+
* and human-readable description. The gate uses the category for UI
|
|
6
|
+
* badge color; the description appears in the permission prompt modal.
|
|
7
|
+
*
|
|
8
|
+
* `OperationName` is a union of known builtin operations + `(string & {})`
|
|
9
|
+
* for runtime extensions. This gives autocomplete for known ops while
|
|
10
|
+
* allowing {@link registerOperation} to extend at runtime.
|
|
11
|
+
*
|
|
12
|
+
* Builtin operations cover reads (getPullRequests, listBranches, etc.)
|
|
13
|
+
* and writes (createPullRequest, updatePullRequestTitle, and approval
|
|
14
|
+
* rule CRUD: createApprovalRule, updateApprovalRule, deleteApprovalRule).
|
|
15
|
+
*
|
|
16
|
+
* **Common tasks**
|
|
17
|
+
*
|
|
18
|
+
* - Get operation metadata: {@link getOperationMeta}
|
|
19
|
+
* - List all operations: {@link allOperations}
|
|
20
|
+
* - Register new operation: {@link registerOperation}
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export interface OperationMeta {
|
|
25
|
+
readonly category: "read" | "write";
|
|
26
|
+
readonly description: string;
|
|
27
|
+
}
|
|
28
|
+
declare const BuiltinOperations: {
|
|
29
|
+
readonly getCallerIdentity: OperationMeta;
|
|
30
|
+
readonly listRepositories: OperationMeta;
|
|
31
|
+
readonly listPullRequests: OperationMeta;
|
|
32
|
+
readonly getPullRequests: OperationMeta;
|
|
33
|
+
readonly getPullRequest: OperationMeta;
|
|
34
|
+
readonly evaluatePullRequestApprovalRules: OperationMeta;
|
|
35
|
+
readonly getPullRequestApprovalStates: OperationMeta;
|
|
36
|
+
readonly getMergeConflicts: OperationMeta;
|
|
37
|
+
readonly getCommentsForPullRequest: OperationMeta;
|
|
38
|
+
readonly listBranches: OperationMeta;
|
|
39
|
+
readonly getDifferences: OperationMeta;
|
|
40
|
+
readonly createPullRequest: OperationMeta;
|
|
41
|
+
readonly updatePullRequestTitle: OperationMeta;
|
|
42
|
+
readonly updatePullRequestDescription: OperationMeta;
|
|
43
|
+
readonly createApprovalRule: OperationMeta;
|
|
44
|
+
readonly updateApprovalRule: OperationMeta;
|
|
45
|
+
readonly deleteApprovalRule: OperationMeta;
|
|
46
|
+
};
|
|
47
|
+
export type BuiltinOperation = keyof typeof BuiltinOperations;
|
|
48
|
+
export type OperationName = BuiltinOperation | (string & {});
|
|
49
|
+
export declare const getOperationMeta: (name: OperationName) => OperationMeta;
|
|
50
|
+
export declare const allOperations: () => ReadonlyArray<readonly [string, OperationMeta]>;
|
|
51
|
+
export declare const registerOperation: (name: string, meta: OperationMeta) => void;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=operations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../src/PermissionService/operations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAID,QAAA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;CAkB2B,CAAA;AAElD,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,iBAAiB,CAAA;AAG7D,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAM5D,eAAO,MAAM,gBAAgB,GAAI,MAAM,aAAa,KAAG,aAAuD,CAAA;AAE9G,eAAO,MAAM,aAAa,QAAO,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC,CAA8B,CAAA;AAE7G,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,MAAM,aAAa,KAAG,IAErE,CAAA"}
|