@azure/core-client 1.3.3 → 1.4.0-alpha.20220104.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Release History
2
2
 
3
+ ## 1.4.0 (Unreleased)
4
+
5
+ ### Features Added
6
+
7
+ - Added a new function `authorizeRequestOnClaimChallenge`, that can be used with the `@azure/core-rest-pipeline`'s `bearerTokenAuthenticationPolicy` to support [Continuous Access Evaluation (CAE) challenges](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation).
8
+ - Call the `bearerTokenAuthenticationPolicy` with the following options: `bearerTokenAuthenticationPolicy({ authorizeRequestOnChallenge: authorizeRequestOnClaimChallenge })`. Once provided, the `bearerTokenAuthenticationPolicy` policy will internally handle Continuous Access Evaluation (CAE) challenges. When it can't complete a challenge it will return the 401 (unauthorized) response from ARM.
9
+
10
+ ### Breaking Changes
11
+
12
+ ### Bugs Fixed
13
+
14
+ ### Other Changes
15
+
3
16
  ## 1.3.3 (2021-12-02)
4
17
 
5
18
  ### Bugs Fixed
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var coreRestPipeline = require('@azure/core-rest-pipeline');
6
+ var logger = require('@azure/logger');
6
7
  require('@azure/core-asynciterator-polyfill');
7
8
 
8
9
  // Copyright (c) Microsoft Corporation.
@@ -116,7 +117,7 @@ function flattenResponse(fullResponse, responseSpec) {
116
117
  body: fullResponse.parsedBody,
117
118
  headers: parsedHeaders,
118
119
  hasNullableType: isNullable,
119
- shouldWrapBody: isPrimitiveBody(fullResponse.parsedBody, expectedBodyTypeName)
120
+ shouldWrapBody: isPrimitiveBody(fullResponse.parsedBody, expectedBodyTypeName),
120
121
  });
121
122
  }
122
123
 
@@ -140,6 +141,14 @@ function encodeByteArray(value) {
140
141
  function decodeString(value) {
141
142
  return Buffer.from(value, "base64");
142
143
  }
144
+ /**
145
+ * Decodes a base64 string into a string.
146
+ * @param value - the base64 string to decode
147
+ * @internal
148
+ */
149
+ function decodeStringToString(value) {
150
+ return Buffer.from(value, "base64").toString();
151
+ }
143
152
 
144
153
  // Copyright (c) Microsoft Corporation.
145
154
  // Licensed under the MIT license.
@@ -163,7 +172,7 @@ class SerializerImpl {
163
172
  throw new Error(`"${objectName}" with value "${value}" should satisfy the constraint "${constraintName}": ${constraintValue}.`);
164
173
  };
165
174
  if (mapper.constraints && value !== undefined && value !== null) {
166
- const { ExclusiveMaximum, ExclusiveMinimum, InclusiveMaximum, InclusiveMinimum, MaxItems, MaxLength, MinItems, MinLength, MultipleOf, Pattern, UniqueItems } = mapper.constraints;
175
+ const { ExclusiveMaximum, ExclusiveMinimum, InclusiveMaximum, InclusiveMinimum, MaxItems, MaxLength, MinItems, MinLength, MultipleOf, Pattern, UniqueItems, } = mapper.constraints;
167
176
  if (ExclusiveMaximum !== undefined && value >= ExclusiveMaximum) {
168
177
  failValidation("ExclusiveMaximum", ExclusiveMaximum);
169
178
  }
@@ -222,8 +231,8 @@ class SerializerImpl {
222
231
  xml: {
223
232
  rootName: (_a = options.xml.rootName) !== null && _a !== void 0 ? _a : "",
224
233
  includeRoot: (_b = options.xml.includeRoot) !== null && _b !== void 0 ? _b : false,
225
- xmlCharKey: (_c = options.xml.xmlCharKey) !== null && _c !== void 0 ? _c : XML_CHARKEY
226
- }
234
+ xmlCharKey: (_c = options.xml.xmlCharKey) !== null && _c !== void 0 ? _c : XML_CHARKEY,
235
+ },
227
236
  };
228
237
  let payload = {};
229
238
  const mapperType = mapper.type.name;
@@ -311,8 +320,8 @@ class SerializerImpl {
311
320
  xml: {
312
321
  rootName: (_a = options.xml.rootName) !== null && _a !== void 0 ? _a : "",
313
322
  includeRoot: (_b = options.xml.includeRoot) !== null && _b !== void 0 ? _b : false,
314
- xmlCharKey: (_c = options.xml.xmlCharKey) !== null && _c !== void 0 ? _c : XML_CHARKEY
315
- }
323
+ xmlCharKey: (_c = options.xml.xmlCharKey) !== null && _c !== void 0 ? _c : XML_CHARKEY,
324
+ },
316
325
  };
317
326
  if (responseBody === undefined || responseBody === null) {
318
327
  if (this.isXML && mapper.type.name === "Sequence" && !mapper.xmlIsWrapped) {
@@ -417,9 +426,7 @@ function bufferToBase64Url(buffer) {
417
426
  // Uint8Array to Base64.
418
427
  const str = encodeByteArray(buffer);
419
428
  // Base64 to Base64Url.
420
- return trimEnd(str, "=")
421
- .replace(/\+/g, "-")
422
- .replace(/\//g, "_");
429
+ return trimEnd(str, "=").replace(/\+/g, "-").replace(/\//g, "_");
423
430
  }
424
431
  function base64UrlToByteArray(str) {
425
432
  if (!str) {
@@ -578,14 +585,21 @@ function serializeDateTypes(typeName, value, objectName) {
578
585
  return value;
579
586
  }
580
587
  function serializeSequenceType(serializer, mapper, object, objectName, isXml, options) {
588
+ var _a;
581
589
  if (!Array.isArray(object)) {
582
590
  throw new Error(`${objectName} must be of type Array.`);
583
591
  }
584
- const elementType = mapper.type.element;
592
+ let elementType = mapper.type.element;
585
593
  if (!elementType || typeof elementType !== "object") {
586
594
  throw new Error(`element" metadata for an Array must be defined in the ` +
587
595
  `mapper and it must of type "object" in ${objectName}.`);
588
596
  }
597
+ // Quirk: Composite mappers referenced by `element` might
598
+ // not have *all* properties declared (like uberParent),
599
+ // so let's try to look up the full definition by name.
600
+ if (elementType.type.name === "Composite" && elementType.type.className) {
601
+ elementType = (_a = serializer.modelMappers[elementType.type.className]) !== null && _a !== void 0 ? _a : elementType;
602
+ }
589
603
  const tempArray = [];
590
604
  for (let i = 0; i < object.length; i++) {
591
605
  const serializedValue = serializer.serialize(elementType, object[i], objectName, options);
@@ -941,8 +955,8 @@ function deserializeDictionaryType(serializer, mapper, responseBody, objectName,
941
955
  return responseBody;
942
956
  }
943
957
  function deserializeSequenceType(serializer, mapper, responseBody, objectName, options) {
944
- /* jshint validthis: true */
945
- const element = mapper.type.element;
958
+ var _a;
959
+ let element = mapper.type.element;
946
960
  if (!element || typeof element !== "object") {
947
961
  throw new Error(`element" metadata for an Array must be defined in the ` +
948
962
  `mapper and it must of type "object" in ${objectName}`);
@@ -952,6 +966,12 @@ function deserializeSequenceType(serializer, mapper, responseBody, objectName, o
952
966
  // xml2js will interpret a single element array as just the element, so force it to be an array
953
967
  responseBody = [responseBody];
954
968
  }
969
+ // Quirk: Composite mappers referenced by `element` might
970
+ // not have *all* properties declared (like uberParent),
971
+ // so let's try to look up the full definition by name.
972
+ if (element.type.name === "Composite" && element.type.className) {
973
+ element = (_a = serializer.modelMappers[element.type.className]) !== null && _a !== void 0 ? _a : element;
974
+ }
955
975
  const tempArray = [];
956
976
  for (let i = 0; i < responseBody.length; i++) {
957
977
  tempArray[i] = serializer.deserialize(element, responseBody[i], `${objectName}[${i}]`, options);
@@ -963,8 +983,12 @@ function deserializeSequenceType(serializer, mapper, responseBody, objectName, o
963
983
  function getPolymorphicMapper(serializer, mapper, object, polymorphicPropertyName) {
964
984
  const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper);
965
985
  if (polymorphicDiscriminator) {
966
- const discriminatorName = polymorphicDiscriminator[polymorphicPropertyName];
986
+ let discriminatorName = polymorphicDiscriminator[polymorphicPropertyName];
967
987
  if (discriminatorName) {
988
+ // The serializedName might have \\, which we just want to ignore
989
+ if (polymorphicPropertyName === "serializedName") {
990
+ discriminatorName = discriminatorName.replace(/\\/gi, "");
991
+ }
968
992
  const discriminatorValue = object[discriminatorName];
969
993
  if (discriminatorValue !== undefined && discriminatorValue !== null) {
970
994
  const typeName = mapper.type.uberParent || mapper.type.className;
@@ -1009,7 +1033,7 @@ const MapperTypeNames = {
1009
1033
  String: "String",
1010
1034
  Stream: "Stream",
1011
1035
  TimeSpan: "TimeSpan",
1012
- UnixTime: "UnixTime"
1036
+ UnixTime: "UnixTime",
1013
1037
  };
1014
1038
 
1015
1039
  // Copyright (c) Microsoft Corporation.
@@ -1095,7 +1119,7 @@ function getOperationArgumentValueFromParameter(operationArguments, parameter, f
1095
1119
  const propertyPath = parameterPath[propertyName];
1096
1120
  const propertyValue = getOperationArgumentValueFromParameter(operationArguments, {
1097
1121
  parameterPath: propertyPath,
1098
- mapper: propertyMapper
1122
+ mapper: propertyMapper,
1099
1123
  }, fallbackObject);
1100
1124
  if (propertyValue !== undefined) {
1101
1125
  if (!value) {
@@ -1141,7 +1165,7 @@ const CollectionFormatToDelimiterMap = {
1141
1165
  SSV: " ",
1142
1166
  Multi: "Multi",
1143
1167
  TSV: "\t",
1144
- Pipes: "|"
1168
+ Pipes: "|",
1145
1169
  };
1146
1170
  function getRequestUrl(baseUri, operationSpec, operationArguments, fallbackObject) {
1147
1171
  const urlReplacements = calculateUrlReplacements(operationSpec, operationArguments, fallbackObject);
@@ -1276,7 +1300,7 @@ function calculateQueryParameters(operationSpec, operationArguments, fallbackObj
1276
1300
  }
1277
1301
  return {
1278
1302
  queryParams: result,
1279
- sequenceParams
1303
+ sequenceParams,
1280
1304
  };
1281
1305
  }
1282
1306
  function simpleParseQueryParams(queryString) {
@@ -1390,15 +1414,15 @@ function deserializationPolicy(options = {}) {
1390
1414
  xml: {
1391
1415
  rootName: (_e = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.rootName) !== null && _e !== void 0 ? _e : "",
1392
1416
  includeRoot: (_f = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.includeRoot) !== null && _f !== void 0 ? _f : false,
1393
- xmlCharKey: (_g = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.xmlCharKey) !== null && _g !== void 0 ? _g : XML_CHARKEY
1394
- }
1417
+ xmlCharKey: (_g = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.xmlCharKey) !== null && _g !== void 0 ? _g : XML_CHARKEY,
1418
+ },
1395
1419
  };
1396
1420
  return {
1397
1421
  name: deserializationPolicyName,
1398
1422
  async sendRequest(request, next) {
1399
1423
  const response = await next(request);
1400
1424
  return deserializeResponseBody(jsonContentTypes, xmlContentTypes, response, updatedOptions, parseXML);
1401
- }
1425
+ },
1402
1426
  };
1403
1427
  }
1404
1428
  function getOperationResponseMap(parsedResponse) {
@@ -1468,7 +1492,7 @@ async function deserializeResponseBody(jsonContentTypes, xmlContentTypes, respon
1468
1492
  const restError = new coreRestPipeline.RestError(`Error ${deserializeError} occurred in deserializing the responseBody - ${parsedResponse.bodyAsText}`, {
1469
1493
  statusCode: parsedResponse.status,
1470
1494
  request: parsedResponse.request,
1471
- response: parsedResponse
1495
+ response: parsedResponse,
1472
1496
  });
1473
1497
  throw restError;
1474
1498
  }
@@ -1511,7 +1535,7 @@ function handleErrorResponse(parsedResponse, operationSpec, responseSpec) {
1511
1535
  const error = new coreRestPipeline.RestError(initialErrorMessage, {
1512
1536
  statusCode: parsedResponse.status,
1513
1537
  request: parsedResponse.request,
1514
- response: parsedResponse
1538
+ response: parsedResponse,
1515
1539
  });
1516
1540
  // If the item failed but there's no error spec or default spec to deserialize the error,
1517
1541
  // we should fail so we just throw the parsed response
@@ -1548,7 +1572,8 @@ function handleErrorResponse(parsedResponse, operationSpec, responseSpec) {
1548
1572
  }
1549
1573
  // If error response has headers, try to deserialize it using default header mapper
1550
1574
  if (parsedResponse.headers && defaultHeadersMapper) {
1551
- error.response.parsedHeaders = operationSpec.serializer.deserialize(defaultHeadersMapper, parsedResponse.headers.toJSON(), "operationRes.parsedHeaders");
1575
+ error.response.parsedHeaders =
1576
+ operationSpec.serializer.deserialize(defaultHeadersMapper, parsedResponse.headers.toJSON(), "operationRes.parsedHeaders");
1552
1577
  }
1553
1578
  }
1554
1579
  catch (defaultError) {
@@ -1587,7 +1612,7 @@ async function parse(jsonContentTypes, xmlContentTypes, operationResponse, opts,
1587
1612
  code: errCode,
1588
1613
  statusCode: operationResponse.status,
1589
1614
  request: operationResponse.request,
1590
- response: operationResponse
1615
+ response: operationResponse,
1591
1616
  });
1592
1617
  throw e;
1593
1618
  }
@@ -1617,7 +1642,7 @@ function serializationPolicy(options = {}) {
1617
1642
  serializeRequestBody(request, operationArguments, operationSpec, stringifyXML);
1618
1643
  }
1619
1644
  return next(request);
1620
- }
1645
+ },
1621
1646
  };
1622
1647
  }
1623
1648
  /**
@@ -1662,14 +1687,14 @@ function serializeRequestBody(request, operationArguments, operationSpec, string
1662
1687
  xml: {
1663
1688
  rootName: (_b = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.rootName) !== null && _b !== void 0 ? _b : "",
1664
1689
  includeRoot: (_c = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.includeRoot) !== null && _c !== void 0 ? _c : false,
1665
- xmlCharKey: (_d = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.xmlCharKey) !== null && _d !== void 0 ? _d : XML_CHARKEY
1666
- }
1690
+ xmlCharKey: (_d = serializerOptions === null || serializerOptions === void 0 ? void 0 : serializerOptions.xml.xmlCharKey) !== null && _d !== void 0 ? _d : XML_CHARKEY,
1691
+ },
1667
1692
  };
1668
1693
  const xmlCharKey = updatedOptions.xml.xmlCharKey;
1669
1694
  if (operationSpec.requestBody && operationSpec.requestBody.mapper) {
1670
1695
  request.body = getOperationArgumentValueFromParameter(operationArguments, operationSpec.requestBody);
1671
1696
  const bodyMapper = operationSpec.requestBody.mapper;
1672
- const { required, serializedName, xmlName, xmlElementName, xmlNamespace, xmlNamespacePrefix, nullable } = bodyMapper;
1697
+ const { required, serializedName, xmlName, xmlElementName, xmlNamespace, xmlNamespacePrefix, nullable, } = bodyMapper;
1673
1698
  const typeName = bodyMapper.type.name;
1674
1699
  try {
1675
1700
  if ((request.body !== undefined && request.body !== null) ||
@@ -1687,7 +1712,7 @@ function serializeRequestBody(request, operationArguments, operationSpec, string
1687
1712
  else if (!isStream) {
1688
1713
  request.body = stringifyXML(value, {
1689
1714
  rootName: xmlName || serializedName,
1690
- xmlCharKey
1715
+ xmlCharKey,
1691
1716
  });
1692
1717
  }
1693
1718
  }
@@ -1755,12 +1780,12 @@ function createClientPipeline(options = {}) {
1755
1780
  if (options.credentialOptions) {
1756
1781
  pipeline.addPolicy(coreRestPipeline.bearerTokenAuthenticationPolicy({
1757
1782
  credential: options.credentialOptions.credential,
1758
- scopes: options.credentialOptions.credentialScopes
1783
+ scopes: options.credentialOptions.credentialScopes,
1759
1784
  }));
1760
1785
  }
1761
1786
  pipeline.addPolicy(serializationPolicy(options.serializationOptions), { phase: "Serialize" });
1762
1787
  pipeline.addPolicy(deserializationPolicy(options.deserializationOptions), {
1763
- phase: "Deserialize"
1788
+ phase: "Deserialize",
1764
1789
  });
1765
1790
  return pipeline;
1766
1791
  }
@@ -1804,7 +1829,7 @@ class ServiceClient {
1804
1829
  // not part of OperationArguments
1805
1830
  const url = getRequestUrl(baseUri, operationSpec, operationArguments, this);
1806
1831
  const request = coreRestPipeline.createPipelineRequest({
1807
- url
1832
+ url,
1808
1833
  });
1809
1834
  request.method = operationSpec.httpMethod;
1810
1835
  const operationInfo = getOperationRequestInfo(request);
@@ -1886,10 +1911,78 @@ function getCredentialScopes(options) {
1886
1911
  return undefined;
1887
1912
  }
1888
1913
 
1914
+ // Copyright (c) Microsoft Corporation.
1915
+ const defaultLogger = logger.createClientLogger("authorizeRequestOnClaimChallenge");
1916
+ /**
1917
+ * Converts: `Bearer a="b", c="d", Bearer d="e", f="g"`.
1918
+ * Into: `[ { a: 'b', c: 'd' }, { d: 'e', f: 'g' } ]`.
1919
+ *
1920
+ * @internal
1921
+ */
1922
+ function parseCAEChallenge(challenges) {
1923
+ const bearerChallenges = `, ${challenges.trim()}`.split(", Bearer ").filter((x) => x);
1924
+ return bearerChallenges.map((challenge) => {
1925
+ const challengeParts = `${challenge.trim()}, `.split('", ').filter((x) => x);
1926
+ const keyValuePairs = challengeParts.map((keyValue) => (([key, value]) => ({ [key]: value }))(keyValue.trim().split('="')));
1927
+ // Key-value pairs to plain object:
1928
+ return keyValuePairs.reduce((a, b) => (Object.assign(Object.assign({}, a), b)), {});
1929
+ });
1930
+ }
1931
+ /**
1932
+ * This function can be used as a callback for the `bearerTokenAuthenticationPolicy` of `@azure/core-rest-pipeline`, to support CAE challenges:
1933
+ * [Continuous Access Evaluation](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation).
1934
+ *
1935
+ * Call the `bearerTokenAuthenticationPolicy` with the following options:
1936
+ *
1937
+ * ```ts
1938
+ * import { bearerTokenAuthenticationPolicy } from "@azure/core-rest-pipeline";
1939
+ * import { authorizeRequestOnClaimChallenge } from "@azure/core-client";
1940
+ *
1941
+ * const bearerTokenAuthenticationPolicy = bearerTokenAuthenticationPolicy({
1942
+ * authorizeRequestOnChallenge: authorizeRequestOnClaimChallenge
1943
+ * });
1944
+ * ```
1945
+ *
1946
+ * Once provided, the `bearerTokenAuthenticationPolicy` policy will internally handle Continuous Access Evaluation (CAE) challenges.
1947
+ * When it can't complete a challenge it will return the 401 (unauthorized) response from ARM.
1948
+ *
1949
+ * Example challenge with claims:
1950
+ *
1951
+ * ```
1952
+ * Bearer authorization_uri="https://login.windows-ppe.net/", error="invalid_token",
1953
+ * error_description="User session has been revoked",
1954
+ * claims="eyJhY2Nlc3NfdG9rZW4iOnsibmJmIjp7ImVzc2VudGlhbCI6dHJ1ZSwgInZhbHVlIjoiMTYwMzc0MjgwMCJ9fX0="
1955
+ * ```
1956
+ */
1957
+ async function authorizeRequestOnClaimChallenge(onChallengeOptions) {
1958
+ const { scopes, response } = onChallengeOptions;
1959
+ const logger = onChallengeOptions.logger || defaultLogger;
1960
+ const challenge = response.headers.get("WWW-Authenticate");
1961
+ if (!challenge) {
1962
+ logger.info(`The WWW-Authenticate header was missing. Failed to perform the Continuous Access Evaluation authentication flow.`);
1963
+ return false;
1964
+ }
1965
+ const challenges = parseCAEChallenge(challenge) || [];
1966
+ const parsedChallenge = challenges.find((x) => x.claims);
1967
+ if (!parsedChallenge) {
1968
+ logger.info(`The WWW-Authenticate header was missing the necessary "claims" to perform the Continuous Access Evaluation authentication flow.`);
1969
+ return false;
1970
+ }
1971
+ const accessToken = await onChallengeOptions.getAccessToken(parsedChallenge.scope ? [parsedChallenge.scope] : scopes, {
1972
+ claims: decodeStringToString(parsedChallenge.claims),
1973
+ });
1974
+ if (!accessToken) {
1975
+ return false;
1976
+ }
1977
+ onChallengeOptions.request.headers.set("Authorization", `Bearer ${accessToken.token}`);
1978
+ return true;
1979
+ }
1980
+
1889
1981
  exports.MapperTypeNames = MapperTypeNames;
1890
1982
  exports.ServiceClient = ServiceClient;
1891
1983
  exports.XML_ATTRKEY = XML_ATTRKEY;
1892
1984
  exports.XML_CHARKEY = XML_CHARKEY;
1985
+ exports.authorizeRequestOnClaimChallenge = authorizeRequestOnClaimChallenge;
1893
1986
  exports.createClientPipeline = createClientPipeline;
1894
1987
  exports.createSerializer = createSerializer;
1895
1988
  exports.deserializationPolicy = deserializationPolicy;