forked from Vero-protocol/vero-guardian-dashboard
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroleClient.ts
More file actions
146 lines (121 loc) · 3.97 KB
/
Copy pathroleClient.ts
File metadata and controls
146 lines (121 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
export type UserRole = 'admin' | 'guardian' | 'unauthorized';
import { DEFAULT_HORIZON_URL } from './rpc';
const INACTIVE_MARKER_VALUES: Record<string, true> = {
false: true,
'0': true,
no: true,
inactive: true,
unauthorized: true,
};
type RoleMarkerPrefix = 'admin' | 'admins' | 'guardian' | 'guardians';
const ADMIN_MARKER_PREFIXES: readonly RoleMarkerPrefix[] = ['admin', 'admins'];
const GUARDIAN_MARKER_PREFIXES: readonly RoleMarkerPrefix[] = ['guardian', 'guardians'];
function decodeAccountDataValue(encodedValue: string): string | null {
try {
const normalizedValue = encodedValue.trim();
if (
normalizedValue.length % 4 === 1 ||
!/^[A-Za-z0-9+/]*={0,2}$/.test(normalizedValue)
) {
return null;
}
if (typeof Buffer !== 'undefined') {
return Buffer.from(normalizedValue, 'base64').toString('utf8');
}
if (typeof globalThis.atob === 'function') {
const binaryValue = globalThis.atob(normalizedValue);
const bytes = Uint8Array.from(binaryValue, (character) => character.charCodeAt(0));
if (typeof TextDecoder !== 'undefined') {
return new TextDecoder().decode(bytes);
}
return binaryValue;
}
} catch {
return null;
}
return null;
}
function getDecodedValue(data: Record<string, string | undefined>, key: string): string | null {
const encodedValue = data[key];
if (typeof encodedValue !== 'string') {
return null;
}
return decodeAccountDataValue(encodedValue);
}
function isActiveMarker(data: Record<string, string | undefined>, key: string): boolean {
const decodedValue = getDecodedValue(data, key);
if (decodedValue === null) {
return false;
}
return INACTIVE_MARKER_VALUES[decodedValue.trim().toLowerCase()] !== true;
}
function readRoleFromDataKey(
data: Record<string, string | undefined>,
key: string
): UserRole | null {
const decodedValue = getDecodedValue(data, key)?.trim().toLowerCase();
if (decodedValue === 'admin' || decodedValue === 'guardian') {
return decodedValue;
}
return null;
}
function readScopedRole(
data: Record<string, string | undefined>,
publicKey: string
): UserRole | null {
const colonRole = readRoleFromDataKey(data, `role:${publicKey}`);
if (colonRole) {
return colonRole;
}
return readRoleFromDataKey(data, `role_${publicKey}`);
}
function hasScopedActiveRoleMarker(
data: Record<string, string | undefined>,
publicKey: string,
prefixes: readonly RoleMarkerPrefix[]
): boolean {
for (const prefix of prefixes) {
if (
isActiveMarker(data, `${prefix}:${publicKey}`) ||
isActiveMarker(data, `${prefix}_${publicKey}`)
) {
return true;
}
}
return false;
}
export async function fetchUserRole(
publicKey: string,
horizonUrl: string = DEFAULT_HORIZON_URL
): Promise<UserRole> {
const registryAccount = process.env.NEXT_PUBLIC_ROLE_REGISTRY_ACCOUNT?.trim();
const accountId = registryAccount || publicKey;
const response = await fetch(
`${horizonUrl.replace(/\/+$/, '')}/accounts/${encodeURIComponent(accountId)}`
);
if (!response.ok) {
throw new Error(`Unable to load Stellar role account ${accountId}: ${response.status}`);
}
const account = (await response.json()) as { data_attr?: Record<string, string | undefined> };
const data = account.data_attr ?? {};
const usesRegistryAccount = Boolean(registryAccount);
const accountRole = usesRegistryAccount ? null : readRoleFromDataKey(data, 'role');
const scopedRole = readScopedRole(data, publicKey);
if (
hasScopedActiveRoleMarker(data, publicKey, ADMIN_MARKER_PREFIXES) ||
scopedRole === 'admin' ||
(!usesRegistryAccount && isActiveMarker(data, 'admin')) ||
accountRole === 'admin'
) {
return 'admin';
}
if (
hasScopedActiveRoleMarker(data, publicKey, GUARDIAN_MARKER_PREFIXES) ||
scopedRole === 'guardian' ||
(!usesRegistryAccount && isActiveMarker(data, 'guardian')) ||
accountRole === 'guardian'
) {
return 'guardian';
}
return 'unauthorized';
}