Skip to content

Commit e80f383

Browse files
committed
Merge branch 'main' into v-nyerkala/sample/multiple-dial-out
2 parents 04d7f22 + 7c5a988 commit e80f383

File tree

53 files changed

+8759
-481
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+8759
-481
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Entra ID Support Client</title>
6+
</head>
7+
<body>
8+
<div id="root"></div>
9+
<script type="module" src="/src/main.jsx"></script>
10+
</body>
11+
</html>
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
const crypto = require('crypto');
2+
const https = require('https');
3+
const { URL } = require('url');
4+
// Replace <ACS_Resource_Endpoint> and <ACS_Resource_Key> with your actual ACS resource endpoint and key
5+
// You can find these in the Azure portal under your Communication Services resource
6+
const endpoint = "<ACS_Resource_Endpoint>";
7+
const key = "<ACS_Resource_Key>";
8+
const apiVersion = '2025-03-02-preview';
9+
10+
async function main() {
11+
const objectId = "<OBJECT_ID>"; // Replace with the actual object ID
12+
const tenantId = "<TENANT_ID>"; // Replace with the actual tenant ID
13+
const clientId = "<CLIENT_ID>"; // Replace with the actual client ID
14+
const assignment = {
15+
tenantId: tenantId,
16+
// Type of the principal accessing the resource. Possible values are: "user", "group" or "tenant".
17+
principalType: "user",
18+
clientIds: [clientId],
19+
};
20+
try {
21+
// List all assignments
22+
const assignments = await listAssignments();
23+
console.log("Assignments:", assignments);
24+
25+
// Create or update an assignment
26+
const createResponse = await createOrUpdateAssignment(objectId, assignment);
27+
console.log("Create or Update Assignment Response:", createResponse);
28+
29+
// Get a specific assignment by objectId
30+
const getResponse = await getAssignment(objectId);
31+
console.log("Get Assignment Response:", getResponse);
32+
33+
// Update assignments
34+
const updateAssignmentsDict = {};
35+
updateAssignmentsDict[objectId] = {
36+
principalType: "group",
37+
tenantId: tenantId,
38+
clientIds: [clientId],
39+
};
40+
const updateResponse = await updateAssignments(updateAssignmentsDict);
41+
console.log("Update Assignments Response:", updateResponse);
42+
43+
// Delete an assignment by objectId
44+
const deleteResponse = await deleteAssignment(objectId);
45+
console.log("Delete Assignment Response:", deleteResponse);
46+
47+
} catch (error) {
48+
console.error("Error:", error);
49+
}
50+
}
51+
52+
function listAssignments() {
53+
const apiUrl = endpoint.replace(/\/+$/, '') + '/access/entra/assignments?api-version=' + apiVersion;
54+
const urlObj = new URL(apiUrl);
55+
const getMethod = 'GET';
56+
57+
const options = {
58+
hostname: urlObj.hostname,
59+
path: urlObj.pathname + urlObj.search,
60+
method: getMethod,
61+
headers: generateSignedHeaders({
62+
url: apiUrl,
63+
method: getMethod,
64+
body: ''
65+
})
66+
};
67+
68+
return sendRequest(options);
69+
}
70+
71+
async function createOrUpdateAssignment(objectId, assignment) {
72+
const path = `/access/entra/assignments/${objectId}?api-version=${apiVersion}`;
73+
const apiUrl = endpoint.replace(/\/+$/, '') + path;
74+
const urlObj = new URL(apiUrl);
75+
const putMethod = 'PUT';
76+
const body = JSON.stringify(assignment);
77+
const options = {
78+
hostname: urlObj.hostname,
79+
path: urlObj.pathname + urlObj.search,
80+
method: putMethod,
81+
headers: generateSignedHeaders({
82+
url: apiUrl,
83+
method: putMethod,
84+
body: body
85+
})
86+
};
87+
88+
return sendRequest(options, body);
89+
}
90+
91+
async function updateAssignments(assignments) {
92+
const path = `/access/entra/assignments?api-version=${apiVersion}`;
93+
const apiUrl = endpoint.replace(/\/+$/, '') + path;
94+
const urlObj = new URL(apiUrl);
95+
const patchMethod = 'PATCH';
96+
const body = JSON.stringify(assignments);
97+
98+
const options = {
99+
hostname: urlObj.hostname,
100+
path: urlObj.pathname + urlObj.search,
101+
method: patchMethod,
102+
headers: generateSignedHeaders({
103+
url: apiUrl,
104+
method: patchMethod,
105+
body: body,
106+
contentType: "application/merge-patch+json"
107+
})
108+
};
109+
110+
return sendRequest(options, body);
111+
}
112+
113+
async function getAssignment(objectId) {
114+
const path = `/access/entra/assignments/${objectId}?api-version=${apiVersion}`;
115+
const apiUrl = endpoint.replace(/\/+$/, '') + path;
116+
const urlObj = new URL(apiUrl);
117+
const getMethod = 'GET';
118+
119+
const options = {
120+
hostname: urlObj.hostname,
121+
path: urlObj.pathname + urlObj.search,
122+
method: getMethod,
123+
headers: generateSignedHeaders({
124+
url: apiUrl,
125+
method: getMethod,
126+
body: ''
127+
})
128+
};
129+
130+
return sendRequest(options);
131+
}
132+
133+
async function deleteAssignment(objectId) {
134+
const path = `/access/entra/assignments/${objectId}?api-version=${apiVersion}`;
135+
const apiUrl = endpoint.replace(/\/+$/, '') + path;
136+
const urlObj = new URL(apiUrl);
137+
const deleteMethod = 'DELETE';
138+
139+
const options = {
140+
hostname: urlObj.hostname,
141+
path: urlObj.pathname + urlObj.search,
142+
method: deleteMethod,
143+
headers: generateSignedHeaders({
144+
url: apiUrl,
145+
method: deleteMethod,
146+
body: ''
147+
})
148+
};
149+
150+
return sendRequest(options);
151+
}
152+
153+
function generateSignedHeaders({ url, method = 'GET', body = '', contentType = 'application/json' }) {
154+
const verb = method.toUpperCase();
155+
const utcNow = new Date().toUTCString();
156+
const contentHash = crypto.createHash('sha256').update(body, 'utf8').digest('base64');
157+
const dateHeader = "x-ms-date";
158+
const signedHeaders = `${dateHeader};host;x-ms-content-sha256`;
159+
160+
const urlObj = new URL(url);
161+
const query = urlObj.searchParams.toString();
162+
const urlPathAndQuery = query ? `${urlObj.pathname}?${query}` : urlObj.pathname;
163+
const port = urlObj.port;
164+
const hostAndPort = port ? `${urlObj.host}:${port}` : urlObj.host;
165+
166+
const stringToSign = `${verb}\n${urlPathAndQuery}\n${utcNow};${hostAndPort};${contentHash}`;
167+
const signature = crypto.createHmac('sha256', Buffer.from(key, 'base64')).update(stringToSign, 'utf8').digest('base64');
168+
169+
return {
170+
Host: hostAndPort,
171+
[dateHeader]: utcNow,
172+
"x-ms-content-sha256": contentHash,
173+
"Content-Type": contentType,
174+
"Authorization": `HMAC-SHA256 SignedHeaders=${signedHeaders}&Signature=${signature}`
175+
};
176+
}
177+
178+
function sendRequest(requestOptions, body = '') {
179+
return new Promise((resolve, reject) => {
180+
const req = https.request(requestOptions, (res) => {
181+
let data = '';
182+
res.on('data', (chunk) => { data += chunk; });
183+
res.on('end', () => {
184+
try {
185+
const json = data ? JSON.parse(data) : {};
186+
resolve(json);
187+
} catch (err) {
188+
reject(err);
189+
}
190+
});
191+
});
192+
193+
req.on('error', (err) => {
194+
reject(err);
195+
});
196+
197+
// Write body if present and not empty
198+
if (body && body.length > 0) {
199+
req.write(body);
200+
}
201+
202+
req.end();
203+
});
204+
}
205+
206+
main();

0 commit comments

Comments
 (0)