@alauda-fe/common
Version:
Alauda frontend team common codes.
186 lines • 26.9 kB
JavaScript
import { HttpResponse, } from '@angular/common/http';
import { Inject, Injectable, isDevMode, Optional } from '@angular/core';
import aes from 'crypto-js/aes';
import encBase64 from 'crypto-js/enc-base64';
import encHex from 'crypto-js/enc-hex';
import encUtf8 from 'crypto-js/enc-utf8';
import formatHex from 'crypto-js/format-hex';
import hmacSha256 from 'crypto-js/hmac-sha256';
import modeCtr from 'crypto-js/mode-ctr';
// cspell: disable-next-line
import noPadding from 'crypto-js/pad-nopadding';
import { catchError, map, of, tap, throwError } from 'rxjs';
import { v4 } from 'uuid';
import { CRYPTO_HEADER_KEY, CRYPTO_KEY, CRYPTO_RANDOM_HEADER_KEY, CRYPTO_TYPE, TOKEN_CRYPTO_INTERCEPTOR_URL_REGEXPS, } from '../core/constants/public-api';
import { SEARCH_URL_PREFIX } from './k8s-api.service';
import * as i0 from "@angular/core";
const defaultEnableUrlRegexps = [
/\/api\/v1\/(.*\/)?secrets/i,
/\/apis\/platform.tkestack.io\/v1\/clusters(\/.*)?/i,
/\/apis\/platform.tkestack.io\/v1\/clustercredentials/i,
/\/apis\/platform.tkestack.io\/v1\/machines/i,
];
const isStandaloneApi = (url) => {
let path = url.match(/((https?:)?\/\/)?[^/]*(\/[^?]*)/)?.[3];
if (path && isDevMode()) {
// 开发模式下,需要把APIGateway移除
// 正常情况不会将前缀设置成多层路径,所以直接移除第一层路径
path = path.replace(/^\/[^/]*/, '');
}
const regex = new RegExp(`^(${SEARCH_URL_PREFIX})?/(apis?|kubernetes)/`);
return path && regex.test(path);
};
const defaultEnable = (req, extra = []) => {
return [...defaultEnableUrlRegexps, ...extra].some(regexp => regexp.test(req.url) && isStandaloneApi(req.url));
};
// 可选的 api 加密拦截器,目前仅支持以/api /apis /kubernetes/ 开头的API
// 后端方案:https://confluence.alauda.cn/pages/viewpage.action?pageId=163054340
// 如果使用 http 则通过设置 headers 来选择加密策略,key 为 CRYPTO_HEADER_KEY
// 如果使用 k8sApi 则通过 crypto 来选择加密策略
// 可以通过 TOKEN_CRYPTO_INTERCEPTOR_URL_REGEXPS 注入默认的开启加密匹配正则
export class CryptoInterceptorService {
constructor(extraUrlRegexps) {
this.extraUrlRegexps = extraUrlRegexps;
}
factoryContentType(req) {
if (!req.headers.has('Content-Type')) {
const detectedType = req.detectContentTypeHeader();
if (detectedType !== null) {
req = req.clone({
headers: req.headers.set('Content-Type', detectedType),
});
}
}
return req;
}
encryptPayload(req) {
if (req.headers.get(CRYPTO_HEADER_KEY) && req.body) {
let bodyStr = '';
// 只对json和字符串类型的payload加密
if (typeof req.body === 'string') {
bodyStr = req.body;
}
else {
try {
bodyStr = JSON.stringify(req.body);
}
catch { }
}
if (bodyStr) {
// 将 angular 自动识别的逻辑加到这里来,避免加密后被统一设成 text
req = this.factoryContentType(req);
const random = v4().replaceAll('-', '');
req = req.clone({
headers: req.headers.set(CRYPTO_RANDOM_HEADER_KEY, random),
body: encrypt(bodyStr, random),
});
}
}
return req;
}
factoryReq(req) {
if (!req.headers.get(CRYPTO_HEADER_KEY) &&
defaultEnable(req, this.extraUrlRegexps)) {
req = req.clone({
headers: req.headers.set(CRYPTO_HEADER_KEY, CRYPTO_TYPE.Enable),
});
}
req = this.encryptPayload(req);
return req;
}
/**
* 实现方案:
* 1. 请求和响应处理分开处理,在请求的header上加上加密标识,根据返回的响应header处理对应的加密策略,而不是使用请求时使用的策略。这样能规避服务端的主动加密策略(前端没有使用加密)
* 2. 如果解密失败,将不做任何处理,原样返回,因为理论上解密不应该失败,如果失败可能是不需要解密,最主要的是返回解密失败的信息比原样返回对业务来说更没意义
*/
intercept(req, next) {
req = this.factoryReq(req);
// 当是 progress ,例如 watch 场景的变更,不能获取 responseHeader,所以需要先在其他状态及时记录 responseHeader
let responseHeader;
return next.handle(req).pipe(tap((event) => {
responseHeader = event.headers || responseHeader;
}), map(event => {
const random = responseHeader?.get(CRYPTO_RANDOM_HEADER_KEY);
// 如果不是正常的响应
if (!(event instanceof HttpResponse)) {
if (event.type === 3 && random) {
const progressEvent = event;
try {
progressEvent.partialText = decrypt(random, progressEvent.partialText);
}
catch { }
return progressEvent;
}
return event;
}
// 如果是正常的响应
if (!random || !event.body) {
return event;
}
try {
return event.clone({
body: decrypt(random, event.body),
});
}
catch { }
return event;
}), catchError((err) => {
// 加密后返回的是字符串,所以如果请求的是json则一定是error的(如果 responseType 是 json,则无论响应的 content-type 是不是 json 都按照json解析)
const random = err?.headers?.get(CRYPTO_RANDOM_HEADER_KEY);
if (random &&
req.responseType === 'json' &&
typeof err.error.text === 'string') {
try {
// 如果存在加密标识且请求的是json,则这个错误基本上就是因为解析json导致的,所以尝试解密后进行json解析
const json = JSON.parse(decrypt(random, err.error.text));
// 如果解析成功则其就应该是一个正常的返回,所以生成一个正常的 HttpResponse
return of(new HttpResponse({
body: json,
status: err.status,
headers: err.headers,
statusText: err.statusText,
url: err.url,
}));
}
catch {
return throwError(() => err);
}
}
return throwError(() => err);
}));
}
static { this.ɵfac = function CryptoInterceptorService_Factory(t) { return new (t || CryptoInterceptorService)(i0.ɵɵinject(TOKEN_CRYPTO_INTERCEPTOR_URL_REGEXPS, 8)); }; }
static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: CryptoInterceptorService, factory: CryptoInterceptorService.ɵfac }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(CryptoInterceptorService, [{
type: Injectable
}], () => [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [TOKEN_CRYPTO_INTERCEPTOR_URL_REGEXPS]
}] }], null); })();
function decrypt(random, content) {
// 先用 hex 把 content 解密
const contentHex = encHex.parse(content.replaceAll('\n', ''));
// 随机数经过HMAC-sha256后取前端16字节,HMAC密钥为字符串“alauda”
const key = encHex.parse(hmacSha256(random, CRYPTO_KEY).toString().slice(0, 32));
return aes
.decrypt(encBase64.stringify(contentHex), key, {
iv: key,
mode: modeCtr,
padding: noPadding,
})
.toString(encUtf8);
}
function encrypt(content, random) {
const key = encHex.parse(hmacSha256(random, CRYPTO_KEY).toString().slice(0, 32));
return aes
.encrypt(content, key, {
iv: key,
mode: modeCtr,
padding: noPadding,
})
.toString(formatHex);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3J5cHRvLmludGVyY2VwdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vbGlicy9jb21tb24vc3JjL2FwaS9jcnlwdG8uaW50ZXJjZXB0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQVFMLFlBQVksR0FDYixNQUFNLHNCQUFzQixDQUFDO0FBQzlCLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDeEUsT0FBTyxHQUFHLE1BQU0sZUFBZSxDQUFDO0FBQ2hDLE9BQU8sU0FBUyxNQUFNLHNCQUFzQixDQUFDO0FBQzdDLE9BQU8sTUFBTSxNQUFNLG1CQUFtQixDQUFDO0FBQ3ZDLE9BQU8sT0FBTyxNQUFNLG9CQUFvQixDQUFDO0FBQ3pDLE9BQU8sU0FBUyxNQUFNLHNCQUFzQixDQUFDO0FBQzdDLE9BQU8sVUFBVSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sT0FBTyxNQUFNLG9CQUFvQixDQUFDO0FBQ3pDLDRCQUE0QjtBQUM1QixPQUFPLFNBQVMsTUFBTSx5QkFBeUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBYyxFQUFFLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN4RSxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRTFCLE9BQU8sRUFDTCxpQkFBaUIsRUFDakIsVUFBVSxFQUNWLHdCQUF3QixFQUN4QixXQUFXLEVBQ1gsb0NBQW9DLEdBQ3JDLE1BQU0sOEJBQThCLENBQUM7QUFFdEMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7O0FBRXRELE1BQU0sdUJBQXVCLEdBQWE7SUFDeEMsNEJBQTRCO0lBQzVCLG9EQUFvRDtJQUNwRCx1REFBdUQ7SUFDdkQsNkNBQTZDO0NBQzlDLENBQUM7QUFDRixNQUFNLGVB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RUFBRSxPQUFPO1FBQ2IsT0FBTyxFQUFFLFNBQVM7S0FDbkIsQ0FBQztTQUNELFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUN2QixDQUFDO0FBRUQsU0FBUyxPQUFPLENBQUMsT0FBZSxFQUFFLE1BQWM7SUFDOUMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FDdEIsVUFBVSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUN2RCxDQUFDO0lBRUYsT0FBTyxHQUFHO1NBQ1AsT0FBTyxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUU7UUFDckIsRUFBRSxFQUFFLEdBQUc7UUFDUCxJQUFJLEVBQUUsT0FBTztRQUNiLE9BQU8sRUFBRSxTQUFTO0tBQ25CLENBQUM7U0FDRCxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDekIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEh0dHBEb3dubG9hZFByb2dyZXNzRXZlbnQsXG4gIEh0dHBFcnJvclJlc3BvbnNlLFxuICBIdHRwRXZlbnQsXG4gIEh0dHBIYW5kbGVyLFxuICBIdHRwSGVhZGVycyxcbiAgSHR0cEludGVyY2VwdG9yLFxuICBIdHRwUmVxdWVzdCxcbiAgSHR0cFJlc3BvbnNlLFxufSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBJbmplY3QsIEluamVjdGFibGUsIGlzRGV2TW9kZSwgT3B0aW9uYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCBhZXMgZnJvbSAnY3J5cHRvLWpzL2Flcyc7XG5pbXBvcnQgZW5jQmFzZTY0IGZyb20gJ2NyeXB0by1qcy9lbmMtYmFzZTY0JztcbmltcG9ydCBlbmNIZXggZnJvbSAnY3J5cHRvLWpzL2VuYy1oZXgnO1xuaW1wb3J0IGVuY1V0ZjggZnJvbSAnY3J5cHRvLWpzL2VuYy11dGY4JztcbmltcG9ydCBmb3JtYXRIZXggZnJvbSAnY3J5cHRvLWpzL2Zvcm1hdC1oZXgnO1xuaW1wb3J0IGhtYWNTaGEyNTYgZnJvbSAnY3J5cHRvLWpzL2htYWMtc2hhMjU2JztcbmltcG9ydCBtb2RlQ3RyIGZyb20gJ2NyeXB0by1qcy9tb2RlLWN0cic7XG4vLyBjc3BlbGw6IGRpc2FibGUtbmV4dC1saW5lXG5pbXBvcnQgbm9QYWRkaW5nIGZyb20gJ2NyeXB0by1qcy9wYWQtbm9wYWRkaW5nJztcbmltcG9ydCB7IGNhdGNoRXJyb3IsIG1hcCwgT2JzZXJ2YWJsZSwgb2YsIHRhcCwgdGhyb3dFcnJvciB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgdjQgfSBmcm9tICd1dWlkJztcblxuaW1wb3J0IHtcbiAgQ1JZUFRPX0hFQURFUl9LRVksXG4gIENSWVBUT19LRVksXG4gIENSWVBUT19SQU5ET01fSEVBREVSX0tFWSxcbiAgQ1JZUFRPX1RZUEUsXG4gIFRPS0VOX0NSWVBUT19JTlRFUkNFUFRPUl9VUkxfUkVHRVhQUyxcbn0gZnJvbSAnLi4vY29yZS9jb25zdGFudHMvcHVibGljLWFwaSc7XG5cbmltcG9ydCB7IFNFQVJDSF9VUkxfUFJFRklYIH0gZnJvbSAnLi9rOHMtYXBpLnNlcnZpY2UnO1xuXG5jb25zdCBkZWZhdWx0RW5hYmxlVXJsUmVnZXhwczogUmVnRXhwW10gPSBbXG4gIC9cXC9hcGlcXC92MVxcLyguKlxcLyk/c2VjcmV0cy9pLFxuICAvXFwvYXBpc1xcL3BsYXRmb3JtLnRrZXN0YWNrLmlvXFwvdjFcXC9jbHVzdGVycyhcXC8uKik/L2ksXG4gIC9cXC9hcGlzXFwvcGxhdGZvcm0udGtlc3RhY2suaW9cXC92MVxcL2NsdXN0ZXJjcmVkZW50aWFscy9pLFxuICAvXFwvYXBpc1xcL3BsYXRmb3JtLnRrZXN0YWNrLmlvXFwvdjFcXC9tYWNoaW5lcy9pLFxuXTtcbmNvbnN0IGlzU3RhbmRhbG9uZUFwaSA9ICh1cmw6IHN0cmluZykgPT4ge1xuICBsZXQgcGF0aCA9IHVybC5tYXRjaCgvKChodHRwcz86KT9cXC9cXC8pP1teL10qKFxcL1teP10qKS8pPy5bM107XG4gIGlmIChwYXRoICYmIGlzRGV2TW9kZSgpKSB7XG4gICAgLy8g5byA5Y+R5qih5byP5LiL77yM6ZyA6KaB5oqKQVBJR2F0ZXdheeenu+mZpFxuICAgIC8vIOato+W4uOaDheWGteS4jeS8muWwhuWJjee8gOiuvue9ruaIkOWkmuWxgui3r+W+hO+8jOaJgOS7peebtOaOpeenu+mZpOesrOS4gOWxgui3r+W+hFxuICAgIHBhdGggPSBwYXRoLnJlcGxhY2UoL15cXC9bXi9dKi8sICcnKTtcbiAgfVxuICBjb25zdCByZWdleCA9IG5ldyBSZWdFeHAoYF4oJHtTRUFSQ0hfVVJMX1BSRUZJWH0pPy8oYXBpcz98a3ViZXJuZXRlcykvYCk7XG4gIHJldHVybiBwYXRoICYmIHJlZ2V4LnRlc3QocGF0aCk7XG59O1xuY29uc3QgZGVmYXVsdEVuYWJsZSA9IChyZXE6IEh0dHBSZXF1ZXN0PHVua25vd24+LCBleHRyYTogUmVnRXhwW10gPSBbXSkgPT4ge1xuICByZXR1cm4gWy4uLmRlZmF1bHRFbmFibGVVcmxSZWdleHBzLCAuLi5leHRyYV0uc29tZShcbiAgICByZWdleHAgPT4gcmVnZXhwLnRlc3QocmVxLnVybCkgJiYgaXNTdGFuZGFsb25lQXBpKHJlcS51cmwpLFxuICApO1xufTtcblxuLy8g5Y+v6YCJ55qEIGFwaSDliqDlr4bmi6bmiKrlmajvvIznm67liY3ku4XmlK/mjIHku6UvYXBpIC9hcGlzIC9rdWJlcm5ldGVzLyDlvIDlpLTnmoRBUElcbi8vIOWQjuerr+aWueahiO+8mmh0dHBzOi8vY29uZmx1ZW5jZS5hbGF1ZGEuY24vcGFnZXMvdmlld3BhZ2UuYWN0aW9uP3BhZ2VJZD0xNjMwNTQzNDBcbi8vIOWmguaenOS9v+eUqCBodHRwIOWImemAmui/h+iuvue9riBoZWFkZXJzIOadpemAieaLqeWKoOWvhuetlueVpe+8jGtleSDkuLogQ1JZUFRPX0hFQURFUl9LRVlcbi8vIOWmguaenOS9v+eUqCBrOHNBcGkg5YiZ6YCa6L+HIGNyeXB0byDmnaXpgInmi6nliqDlr4bnrZbnlaVcbi8vIOWPr+S7pemAmui/hyBUT0tFTl9DUllQVE9fSU5URVJDRVBUT1JfVVJMX1JFR0VYUFMg5rOo5YWl6buY6K6k55qE5byA5ZCv5Yqg5a+G5Yy56YWN5q2j5YiZXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgQ3J5cHRvSW50ZXJjZXB0b3JTZXJ2aWNlIGltcGxlbWVudHMgSHR0cEludGVyY2VwdG9yIHtcbiAgY29uc3RydWN0b3IoXG4gICAgQE9wdGlvbmFsKClcbiAgICBASW5qZWN0KFRPS0VOX0NSWVBUT19JTlRFUkNFUFRPUl9VUkxfUkVHRVhQUylcbiAgICBwcml2YXRlIHJlYWRvbmx5IGV4dHJhVXJsUmVnZXhwczogUmVnRXhwW10sXG4gICkge31cblxuICBwcml2YXRlIGZhY3RvcnlDb250ZW50VHlwZShyZXE6IEh0dHBSZXF1ZXN0PHVua25vd24+KSB7XG4gICAgaWYgKCFyZXEuaGVhZGVycy5oYXMoJ0NvbnRlbnQtVHlwZScpKSB7XG4gICAgICBjb25zdCBkZXRlY3RlZFR5cGUgPSByZXEuZGV0ZWN0Q29udGVudFR5cGVIZWFkZXIoKTtcbiAgICAgIGlmIChkZXRlY3RlZFR5cGUgIT09IG51bGwpIHtcbiAgICAgICAgcmVxID0gcmVxLmNsb25lKHtcbiAgICAgICAgICBoZWFkZXJzOiByZXEuaGVhZGVycy5zZXQoJ0NvbnRlbnQtVHlwZScsIGRldGVjdGVkVHlwZSksXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmVxO1xuICB9XG5cbiAgcHJpdmF0ZSBlbmNyeXB0UGF5bG9hZChyZXE6IEh0dHBSZXF1ZXN0PHVua25vd24+KSB7XG4gICAgaWYgKHJlcS5oZWFkZXJzLmdldChDUllQVE9fSEVBREVSX0tFWSkgJiYgcmVxLmJvZHkpIHtcbiAgICAgIGxldCBib2R5U3RyID0gJyc7XG4gICAgICAvLyDlj6rlr7lqc29u5ZKM5a2X56ym5Liy57G75Z6L55qEcGF5bG9hZOWKoOWvhlxuICAgICAgaWYgKHR5cGVvZiByZXEuYm9keSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgYm9keVN0ciA9IHJlcS5ib2R5O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBib2R5U3RyID0gSlNPTi5zdHJpbmdpZnkocmVxLmJvZHkpO1xuICAgICAgICB9IGNhdGNoIHt9XG4gICAgICB9XG5cbiAgICAgIGlmIChib2R5U3RyKSB7XG4gICAgICAgIC8vIOWwhiBhbmd1bGFyIOiHquWKqOivhuWIq+eahOmAu+i+keWKoOWIsOi/memHjOadpe+8jOmBv+WFjeWKoOWvhuWQjuiiq+e7n+S4gOiuvuaIkCB0ZXh0XG4gICAgICAgIHJlcSA9IHRoaXMuZmFjdG9yeUNvbnRlbnRUeXBlKHJlcSk7XG5cbiAgICAgICAgY29uc3QgcmFuZG9tID0gdjQoKS5yZXBsYWNlQWxsKCctJywgJycpO1xuICAgICAgICByZXEgPSByZXEuY2xvbmUoe1xuICAgICAgICAgIGhlYWRlcnM6IHJlcS5oZWFkZXJzLnNldChDUllQVE9fUkFORE9NX0hFQURFUl9LRVksIHJhbmRvbSksXG4gICAgICAgICAgYm9keTogZW5jcnlwdChib2R5U3RyLCByYW5kb20pLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcmVxO1xuICB9XG5cbiAgcHJpdmF0ZSBmYWN0b3J5UmVxKHJlcTogSHR0cFJlcXVlc3Q8dW5rbm93bj4pIHtcbiAgICBpZiAoXG4gICAgICAhcmVxLmhlYWRlcnMuZ2V0KENSWVBUT19IRUFERVJfS0VZKSAmJlxuICAgICAgZGVmYXVsdEVuYWJsZShyZXEsIHRoaXMuZXh0cmFVcmxSZWdleHBzKVxuICAgICkge1xuICAgICAgcmVxID0gcmVxLmNsb25lKHtcbiAgICAgICAgaGVhZGVyczogcmVxLmhlYWRlcnMuc2V0KENSWVBUT19IRUFERVJfS0VZLCBDUllQVE9fVFlQRS5FbmFibGUpLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmVxID0gdGhpcy5lbmNyeXB0UGF5bG9hZChyZXEpO1xuXG4gICAgcmV0dXJuIHJlcTtcbiAgfVxuXG4gIC8qKlxuICAgKiDlrp7njrDmlrnmoYjvvJpcbiAgICogMS4g6K+35rGC5ZKM5ZON5bqU5aSE55CG5YiG5byA5aSE55CG77yM5Zyo6K+35rGC55qEaGVhZGVy5LiK5Yqg5LiK5Yqg5a+G5qCH6K+G77yM5qC55o2u6L+U5Zue55qE5ZON5bqUaGVhZGVy5aSE55CG5a+55bqU55qE5Yqg5a+G562W55Wl77yM6ICM5LiN5piv5L2/55So6K+35rGC5pe25L2/55So55qE562W55Wl44CC6L+Z5qC36IO96KeE6YG/5pyN5Yqh56uv55qE5Li75Yqo5Yqg5a+G562W55Wl77yI5YmN56uv5rKh5pyJ5L2/55So5Yqg5a+G77yJXG4gICAqIDIuIOWmguaenOino+WvhuWksei0pe+8jOWwhuS4jeWBmuS7u+S9leWkhOeQhu+8jOWOn+agt+i/lOWbnu+8jOWboOS4uueQhuiuuuS4iuino+WvhuS4jeW6lOivpeWksei0pe+8jOWmguaenOWksei0peWPr+iDveaYr+S4jemcgOimgeino+Wvhu+8jOacgOS4u+imgeeahOaYr+i/lOWbnuino+WvhuWksei0peeahOS/oeaBr+avlOWOn+agt+i/lOWbnuWvueS4muWKoeadpeivtOabtOayoeaEj+S5iVxuICAgKi9cbiAgaW50ZXJjZXB0KFxuICAgIHJlcTogSHR0cFJlcXVlc3Q8dW5rbm93bj4sXG4gICAgbmV4dDogSHR0cEhhbmRsZXIsXG4gICk6IE9ic2VydmFibGU8SHR0cEV2ZW50PHVua25vd24+PiB7XG4gICAgcmVxID0gdGhpcy5mYWN0b3J5UmVxKHJlcSk7XG5cbiAgICAvLyDlvZPmmK8gcHJvZ3Jlc3Mg77yM5L6L5aaCIHdhdGNoIOWcuuaZr+eahOWPmOabtO+8jOS4jeiDveiOt+WPliByZXNwb25zZUhlYWRlcu+8jOaJgOS7pemcgOimgeWFiOWcqOWFtuS7lueKtuaAgeWPiuaXtuiusOW9lSByZXNwb25zZUhlYWRlclxuICAgIGxldCByZXNwb25zZUhlYWRlcjogSHR0cEhlYWRlcnM7XG5cbiAgICByZXR1cm4gbmV4dC5oYW5kbGUocmVxKS5waXBlKFxuICAgICAgdGFwKChldmVudDogSHR0cFJlc3BvbnNlPHVua25vd24+KSA9PiB7XG4gICAgICAgIHJlc3BvbnNlSGVhZGVyID0gZXZlbnQuaGVhZGVycyB8fCByZXNwb25zZUhlYWRlcjtcbiAgICAgIH0pLFxuICAgICAgbWFwKGV2ZW50ID0+IHtcbiAgICAgICAgY29uc3QgcmFuZG9tID0gcmVzcG9uc2VIZWFkZXI/LmdldChDUllQVE9fUkFORE9NX0hFQURFUl9LRVkpO1xuICAgICAgICAvLyDlpoLmnpzkuI3mmK/mraPluLjnmoTlk43lupRcbiAgICAgICAgaWYgKCEoZXZlbnQgaW5zdGFuY2VvZiBIdHRwUmVzcG9uc2UpKSB7XG4gICAgICAgICAgaWYgKChldmVudCBhcyBIdHRwRG93bmxvYWRQcm9ncmVzc0V2ZW50KS50eXBlID09PSAzICYmIHJhbmRvbSkge1xuICAgICAgICAgICAgY29uc3QgcHJvZ3Jlc3NFdmVudCA9IGV2ZW50IGFzIEh0dHBEb3dubG9hZFByb2dyZXNzRXZlbnQ7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBwcm9ncmVzc0V2ZW50LnBhcnRpYWxUZXh0ID0gZGVjcnlwdChcbiAgICAgICAgICAgICAgICByYW5kb20sXG4gICAgICAgICAgICAgICAgcHJvZ3Jlc3NFdmVudC5wYXJ0aWFsVGV4dCxcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0gY2F0Y2gge31cbiAgICAgICAgICAgIHJldHVybiBwcm9ncmVzc0V2ZW50O1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gZXZlbnQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyDlpoLmnpzmmK/mraPluLjnmoTlk43lupRcbiAgICAgICAgaWYgKCFyYW5kb20gfHwgIWV2ZW50LmJvZHkpIHtcbiAgICAgICAgICByZXR1cm4gZXZlbnQ7XG4gICAgICAgIH1cblxuICAgICAgICB0cnkge1xuICAgICAgICAgIHJldHVybiBldmVudC5jbG9uZSh7XG4gICAgICAgICAgICBib2R5OiBkZWNyeXB0KHJhbmRvbSwgZXZlbnQuYm9keSBhcyBzdHJpbmcpLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGNhdGNoIHt9XG4gICAgICAgIHJldHVybiBldmVudDtcbiAgICAgIH0pLFxuICAgICAgY2F0Y2hFcnJvcigoZXJyOiBIdHRwRXJyb3JSZXNwb25zZSkgPT4ge1xuICAgICAgICAvLyDliqDlr4blkI7ov5Tlm57nmoTmmK/lrZfnrKbkuLLvvIzmiYDku6XlpoLmnpzor7fmsYLnmoTmmK9qc29u5YiZ5LiA5a6a5pivZXJyb3LnmoTvvIjlpoLmnpwgcmVzcG9uc2VUeXBlIOaYryBqc29u77yM5YiZ5peg6K665ZON5bqU55qEIGNvbnRlbnQtdHlwZSDmmK/kuI3mmK8ganNvbiDpg73mjInnhadqc29u6Kej5p6Q77yJXG4gICAgICAgIGNvbnN0IHJhbmRvbSA9IGVycj8uaGVhZGVycz8uZ2V0KENSWVBUT19SQU5ET01fSEVBREVSX0tFWSk7XG4gICAgICAgIGlmIChcbiAgICAgICAgICByYW5kb20gJiZcbiAgICAgICAgICByZXEucmVzcG9uc2VUeXBlID09PSAnanNvbicgJiZcbiAgICAgICAgICB0eXBlb2YgZXJyLmVycm9yLnRleHQgPT09ICdzdHJpbmcnXG4gICAgICAgICkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyDlpoLmnpzlrZjlnKjliqDlr4bmoIfor4bkuJTor7fmsYLnmoTmmK9qc29u77yM5YiZ6L+Z5Liq6ZSZ6K+v5Z+65pys5LiK5bCx5piv5Zug5Li66Kej5p6QanNvbuWvvOiHtOeahO+8jOaJgOS7peWwneivleino+WvhuWQjui/m+ihjGpzb27op6PmnpBcbiAgICAgICAgICAgIGNvbnN0IGpzb24gPSBKU09OLnBhcnNlKGRlY3J5cHQocmFuZG9tLCBlcnIuZXJyb3IudGV4dCkpO1xuICAgICAgICAgICAgLy8g5aaC5p6c6Kej5p6Q5oiQ5Yqf5YiZ5YW25bCx5bqU6K+l5piv5LiA5Liq5q2j5bi455qE6L+U5Zue77yM5omA5Lul55Sf5oiQ5LiA5Liq5q2j5bi455qEIEh0dHBSZXNwb25zZVxuICAgICAgICAgICAgcmV0dXJuIG9mKFxuICAgICAgICAgICAgICBuZXcgSHR0cFJlc3BvbnNlKHtcbiAgICAgICAgICAgICAgICBib2R5OiBqc29uLFxuICAgICAgICAgICAgICAgIHN0YXR1czogZXJyLnN0YXR1cyxcbiAgICAgICAgICAgICAgICBoZWFkZXJzOiBlcnIuaGVhZGVycyxcbiAgICAgICAgICAgICAgICBzdGF0dXNUZXh0OiBlcnIuc3RhdHVzVGV4dCxcbiAgICAgICAgICAgICAgICB1cmw6IGVyci51cmwsXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIHJldHVybiB0aHJvd0Vycm9yKCgpID0+IGVycik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aHJvd0Vycm9yKCgpID0+IGVycik7XG4gICAgICB9KSxcbiAgICApO1xuICB9XG59XG5cbmZ1bmN0aW9uIGRlY3J5cHQocmFuZG9tOiBzdHJpbmcsIGNvbnRlbnQ6IHN0cmluZykge1xuICAvLyDlhYjnlKggaGV4IOaKiiBjb250ZW50IOino+WvhlxuICBjb25zdCBjb250ZW50SGV4ID0gZW5jSGV4LnBhcnNlKGNvbnRlbnQucmVwbGFjZUFsbCgnXFxuJywgJycpKTtcbiAgLy8g6ZqP5py65pWw57uP6L+HSE1BQy1zaGEyNTblkI7lj5bliY3nq68xNuWtl+iKgu+8jEhNQUPlr4bpkqXkuLrlrZfnrKbkuLLigJxhbGF1ZGHigJ1cbiAgY29uc3Qga2V5ID0gZW5jSGV4LnBhcnNlKFxuICAgIGhtYWNTaGEyNTYocmFuZG9tLCBDUllQVE9fS0VZKS50b1N0cmluZygpLnNsaWNlKDAsIDMyKSxcbiAgKTtcblxuICByZXR1cm4gYWVzXG4gICAgLmRlY3J5cHQoZW5jQmFzZTY0LnN0cmluZ2lmeShjb250ZW50SGV4KSwga2V5LCB7XG4gICAgICBpdjoga2V5LFxuICAgICAgbW9kZTogbW9kZUN0cixcbiAgICAgIHBhZGRpbmc6IG5vUGFkZGluZyxcbiAgICB9KVxuICAgIC50b1N0cmluZyhlbmNVdGY4KTtcbn1cblxuZnVuY3Rpb24gZW5jcnlwdChjb250ZW50OiBzdHJpbmcsIHJhbmRvbTogc3RyaW5nKSB7XG4gIGNvbnN0IGtleSA9IGVuY0hleC5wYXJzZShcbiAgICBobWFjU2hhMjU2KHJhbmRvbSwgQ1JZUFRPX0tFWSkudG9TdHJpbmcoKS5zbGljZSgwLCAzMiksXG4gICk7XG5cbiAgcmV0dXJuIGFlc1xuICAgIC5lbmNyeXB0KGNvbnRlbnQsIGtleSwge1xuICAgICAgaXY6IGtleSxcbiAgICAgIG1vZGU6IG1vZGVDdHIsXG4gICAgICBwYWRkaW5nOiBub1BhZGRpbmcsXG4gICAgfSlcbiAgICAudG9TdHJpbmcoZm9ybWF0SGV4KTtcbn1cbiJdfQ==