Skip to content

Commit daa00ca

Browse files
authored
Adding/Deleting authorization and telemetry headers (#351)
* Remove Graph headers for non graph urls * removing telemetry information if non graph url * Adding comments * iterable set constructor * remove try catch, remove check before deletion
1 parent a745e31 commit daa00ca

File tree

6 files changed

+130
-77
lines changed

6 files changed

+130
-77
lines changed

spec/core/urlParsing.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ const testCases = {
4343
"/me/$filter=any(Actors, it/ID eq Director/ID)": "https://graph.microsoft.com/v1.0/me/$filter=any(Actors, it/ID eq Director/ID)",
4444
"/me?$whatif": "https://graph.microsoft.com/v1.0/me?$whatif",
4545
"/me/?$filter=any(Actors a, any(a/Movies m, a/ID eq m/Director/ID))": "https://graph.microsoft.com/v1.0/me/?$filter=any(Actors a, any(a/Movies m, a/ID eq m/Director/ID))",
46+
47+
// National cloud deployment urls
48+
"https://graph.microsoft.us/v1.0/me": "https://graph.microsoft.us/v1.0/me",
49+
"https://dod-graph.microsoft.us/beta/me?$filter=a": "https://dod-graph.microsoft.us/beta/me?$filter=a",
4650
};
4751

4852
describe("urlParsing.ts", () => {

spec/middleware/TelemetryHandler.ts

Lines changed: 62 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import { assert } from "chai";
99

10+
import { GRAPH_BASE_URL } from "../../src/Constants";
1011
import { Context } from "../../src/IContext";
1112
import { MiddlewareControl } from "../../src/middleware/MiddlewareControl";
1213
import { FeatureUsageFlag, TelemetryHandlerOptions } from "../../src/middleware/options/TelemetryHandlerOptions";
@@ -26,79 +27,80 @@ describe("TelemetryHandler.ts", () => {
2627
statusText: "OK",
2728
});
2829
it("Should not disturb client-request-id in the header", async () => {
29-
try {
30-
const uuid = "dummy_uuid";
31-
const context: Context = {
32-
request: "url",
33-
options: {
34-
headers: {
35-
"client-request-id": uuid,
36-
},
30+
const uuid = "dummy_uuid";
31+
const context: Context = {
32+
request: GRAPH_BASE_URL,
33+
options: {
34+
headers: {
35+
"client-request-id": uuid,
3736
},
38-
};
39-
dummyHTTPHandler.setResponses([okayResponse]);
40-
await telemetryHandler.execute(context);
41-
assert.equal(context.options.headers["client-request-id"], uuid);
42-
} catch (error) {
43-
throw error;
44-
}
37+
},
38+
};
39+
dummyHTTPHandler.setResponses([okayResponse]);
40+
await telemetryHandler.execute(context);
41+
assert.equal(context.options.headers["client-request-id"], uuid);
4542
});
4643

4744
it("Should create client-request-id if one is not present in the request header", async () => {
48-
try {
49-
const context: Context = {
50-
request: "url",
51-
options: {
52-
headers: {
53-
method: "GET",
54-
},
45+
const context: Context = {
46+
request: "https://GRAPH.microsoft.com:443/",
47+
options: {
48+
headers: {
49+
method: "GET",
5550
},
56-
};
57-
dummyHTTPHandler.setResponses([okayResponse]);
58-
await telemetryHandler.execute(context);
59-
assert.isDefined(context.options.headers["client-request-id"]);
60-
} catch (error) {
61-
throw error;
62-
}
51+
},
52+
};
53+
dummyHTTPHandler.setResponses([okayResponse]);
54+
await telemetryHandler.execute(context);
55+
assert.isDefined(context.options.headers["client-request-id"]);
6356
});
6457

6558
it("Should set sdk version header without feature flag usage if telemetry options is not present", async () => {
66-
try {
67-
const context: Context = {
68-
request: "url",
69-
options: {
70-
headers: {
71-
method: "GET",
72-
},
59+
const context: Context = {
60+
request: GRAPH_BASE_URL,
61+
options: {
62+
headers: {
63+
method: "GET",
7364
},
74-
};
75-
dummyHTTPHandler.setResponses([okayResponse]);
76-
await telemetryHandler.execute(context);
77-
assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION}`);
78-
} catch (error) {
79-
throw error;
80-
}
65+
},
66+
};
67+
dummyHTTPHandler.setResponses([okayResponse]);
68+
await telemetryHandler.execute(context);
69+
assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION}`);
8170
});
8271

8372
it("Should set sdk version header with feature flag", async () => {
84-
try {
85-
const telemetryOptions = new TelemetryHandlerOptions();
86-
telemetryOptions["setFeatureUsage"](FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED);
87-
const context: Context = {
88-
request: "url",
89-
options: {
90-
headers: {
91-
method: "GET",
92-
},
73+
const telemetryOptions = new TelemetryHandlerOptions();
74+
telemetryOptions["setFeatureUsage"](FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED);
75+
const context: Context = {
76+
request: GRAPH_BASE_URL,
77+
options: {
78+
headers: {
79+
method: "GET",
9380
},
94-
middlewareControl: new MiddlewareControl([telemetryOptions]),
95-
};
96-
dummyHTTPHandler.setResponses([okayResponse]);
97-
await telemetryHandler.execute(context);
98-
assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION} (featureUsage=${FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED.toString(16)})`);
99-
} catch (error) {
100-
throw error;
101-
}
81+
},
82+
middlewareControl: new MiddlewareControl([telemetryOptions]),
83+
};
84+
dummyHTTPHandler.setResponses([okayResponse]);
85+
await telemetryHandler.execute(context);
86+
assert.equal(context.options.headers["SdkVersion"], `graph-js/${PACKAGE_VERSION} (featureUsage=${FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED.toString(16)})`);
87+
});
88+
89+
it("Should not set telemetry for non-graph url", async () => {
90+
const context: Context = {
91+
request: "test url",
92+
options: {
93+
headers: {
94+
method: "GET",
95+
},
96+
},
97+
middlewareControl: new MiddlewareControl(),
98+
};
99+
dummyHTTPHandler.setResponses([okayResponse]);
100+
await telemetryHandler.execute(context);
101+
assert.equal(context.options.headers["client-request-id"], undefined);
102+
assert.equal(context.options.headers["SdkVersion"], undefined);
103+
assert.equal(context.options.headers["setFeatureUsage"], undefined);
102104
});
103105
});
104106
/* tslint:enable: no-string-literal */

src/Constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ export const GRAPH_API_VERSION = "v1.0";
2020
* A Default base url for a request
2121
*/
2222
export const GRAPH_BASE_URL = "https://graph.microsoft.com/";
23+
24+
/**
25+
* To hold list of the service root endpoints for Microsoft Graph and Graph Explorer for each national cloud.
26+
* Set(iterable:Object) is not supported in Internet Explorer. The consumer is recommended to use a suitable polyfill.
27+
*/
28+
export const GRAPH_URLS = new Set<string>(["graph.microsoft.com", "graph.microsoft.us", "dod-graph.microsoft.us", "graph.microsoft.de", "microsoftgraph.chinacloudapi.cn"]);

src/GraphRequestUtil.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/**
99
* @module GraphRequestUtil
1010
*/
11-
11+
import { GRAPH_URLS } from "./Constants";
1212
/**
1313
* To hold list of OData query params
1414
*/
@@ -58,3 +58,34 @@ export const serializeContent = (content: any): any => {
5858
}
5959
return content;
6060
};
61+
62+
/**
63+
* Checks if the url is one of the service root endpoints for Microsoft Graph and Graph Explorer.
64+
* @param {string} url - The url to be verified
65+
* @returns {boolean} - Returns true if the url is a Graph URL
66+
*/
67+
export const isGraphURL = (url: string): boolean => {
68+
// Valid Graph URL pattern - https://graph.microsoft.com/{version}/{resource}?{query-parameters}
69+
// Valid Graph URL example - https://graph.microsoft.com/v1.0/
70+
url = url.toLowerCase();
71+
72+
if (url.indexOf("https://") !== -1) {
73+
url = url.replace("https://", "");
74+
75+
// Find where the host ends
76+
const startofPortNoPos = url.indexOf(":");
77+
const endOfHostStrPos = url.indexOf("/");
78+
let hostName = "";
79+
if (endOfHostStrPos !== -1) {
80+
if (startofPortNoPos !== -1 && startofPortNoPos < endOfHostStrPos) {
81+
hostName = url.substring(0, startofPortNoPos);
82+
return GRAPH_URLS.has(hostName);
83+
}
84+
// Parse out the host
85+
hostName = url.substring(0, endOfHostStrPos);
86+
return GRAPH_URLS.has(hostName);
87+
}
88+
}
89+
90+
return false;
91+
};

src/middleware/RedirectHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export class RedirectHandler implements Middleware {
202202
} else {
203203
const redirectUrl: string = this.getLocationHeader(response);
204204
if (!this.isRelativeURL(redirectUrl) && this.shouldDropAuthorizationHeader(response.url, redirectUrl)) {
205-
setRequestHeader(context.request, context.options, RedirectHandler.AUTHORIZATION_HEADER, undefined);
205+
delete context.options.headers[RedirectHandler.AUTHORIZATION_HEADER];
206206
}
207207
await this.updateRequestUrl(redirectUrl, context);
208208
}

src/middleware/TelemetryHandler.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/**
99
* @module TelemetryHandler
1010
*/
11-
11+
import { isGraphURL } from "../GraphRequestUtil";
1212
import { Context } from "../IContext";
1313
import { PACKAGE_VERSION } from "../Version";
1414

@@ -66,21 +66,31 @@ export class TelemetryHandler implements Middleware {
6666
*/
6767
public async execute(context: Context): Promise<void> {
6868
try {
69-
let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER);
70-
if (clientRequestId === null) {
71-
clientRequestId = generateUUID();
72-
setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId);
73-
}
74-
let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`;
75-
let options: TelemetryHandlerOptions;
76-
if (context.middlewareControl instanceof MiddlewareControl) {
77-
options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions;
78-
}
79-
if (typeof options !== "undefined") {
80-
const featureUsage: string = options.getFeatureUsage();
81-
sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`;
69+
if (typeof context.request === "string") {
70+
if (isGraphURL(context.request)) {
71+
// Add telemetry only if the request url is a Graph URL.
72+
// Errors are reported as in issue #265 if headers are present when redirecting to a non Graph URL
73+
let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER);
74+
if (clientRequestId === null) {
75+
clientRequestId = generateUUID();
76+
setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId);
77+
}
78+
let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`;
79+
let options: TelemetryHandlerOptions;
80+
if (context.middlewareControl instanceof MiddlewareControl) {
81+
options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions;
82+
}
83+
if (options) {
84+
const featureUsage: string = options.getFeatureUsage();
85+
sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`;
86+
}
87+
appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue);
88+
} else {
89+
// Remove telemetry headers if present during redirection.
90+
delete context.options.headers[TelemetryHandler.CLIENT_REQUEST_ID_HEADER];
91+
delete context.options.headers[TelemetryHandler.SDK_VERSION_HEADER];
92+
}
8293
}
83-
appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue);
8494
return await this.nextMiddleware.execute(context);
8595
} catch (error) {
8696
throw error;

0 commit comments

Comments
 (0)