UNPKG

cnpmcore

Version:

Private NPM Registry for Enterprise

262 lines 20.9 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { Type } from '@eggjs/typebox-validate/typebox'; import { HTTPContext, Context, HTTPBody, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, Inject } from 'egg'; import { ForbiddenError, UnauthorizedError } from 'egg/errors'; import { TokenType, isGranularToken } from "../../core/entity/Token.js"; import { AbstractController } from "./AbstractController.js"; // Creating and viewing access tokens // https://docs.npmjs.com/creating-and-viewing-access-tokens#viewing-access-tokens const TokenOptionsRule = Type.Object({ password: Type.String({ minLength: 8, maxLength: 100 }), readonly: Type.Optional(Type.Boolean()), automation: Type.Optional(Type.Boolean()), // only allow 10 ip for now cidr_whitelist: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 10 })), }); const GranularTokenOptionsRule = Type.Object({ automation: Type.Optional(Type.Boolean()), readonly: Type.Optional(Type.Boolean()), cidr_whitelist: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 10 })), name: Type.String({ maxLength: 255 }), description: Type.Optional(Type.String({ maxLength: 255 })), allowedScopes: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 50 })), allowedPackages: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 50 })), expires: Type.Number({ minimum: 1, maximum: 365 }), }); let TokenController = class TokenController extends AbstractController { // https://github.com/npm/npm-profile/blob/main/lib/index.js#L233 async createToken(ctx, tokenOptions) { const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); ctx.tValidate(TokenOptionsRule, tokenOptions); if (!this.userService.checkPassword(authorizedUser, tokenOptions.password)) { throw new UnauthorizedError('Invalid password'); } const token = await this.userService.createToken(authorizedUser.userId, { isReadonly: tokenOptions.readonly, isAutomation: tokenOptions.automation, cidrWhitelist: tokenOptions.cidr_whitelist, }); return { token: token.token, key: token.tokenKey, cidr_whitelist: token.cidrWhitelist, readonly: token.isReadonly, automation: token.isAutomation, created: token.createdAt, updated: token.updatedAt, }; } // https://github.com/npm/npm-profile/blob/main/lib/index.js#L224 async removeToken(ctx, tokenKey) { const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); await this.userService.removeToken(authorizedUser.userId, tokenKey); return { ok: true }; } // https://github.com/npm/npm-profile/blob/main/lib/index.js#L220 async listTokens(ctx) { // { // 'user-agent': 'npm/8.1.2 node/v16.13.1 darwin arm64 workspaces/false', // 'npm-command': 'token', // authorization: 'Bearer token-value', // accept: '*/*', // 'accept-encoding': 'gzip,deflate', // host: 'localhost:7001', // connection: 'keep-alive' // } const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); const tokens = await this.userRepository.listTokens(authorizedUser.userId); // { // "objects": [ // { // "token": "npm_0i", // "key": "fd69297400579a2ff8b0f224e67214d326ce9bfaf72509cd57c0be2fe6c4a6434b4c4f9f318416569e5ab7c535d12bde5e29ec386373a73c6b2ce2988dc26a22", // "cidr_whitelist": null, // "readonly": false, // "automation": false, // "created": "2021-12-04T17:23:39.744Z", // "updated": "2021-12-04T17:23:39.744Z" // } // ], // "total": 2, // "urls": {} // } const objects = tokens .filter((token) => !isGranularToken(token)) .map((token) => ({ token: token.tokenMark, key: token.tokenKey, cidr_whitelist: token.cidrWhitelist, readonly: token.isReadonly, automation: token.isAutomation, created: token.createdAt, lastUsedAt: token.lastUsedAt, updated: token.updatedAt, })); // TODO: paging, urls: { next: string } return { objects, total: objects.length, urls: {} }; } async ensureWebUser(ip = '') { const userRes = await this.authAdapter.ensureCurrentUser(); if (!userRes?.name || !userRes?.email) { throw new ForbiddenError('need login first'); } const user = await this.userService.findOrCreateUser({ name: userRes.name, email: userRes.email, ip, }); return user; } // Create granular access token through HTTP interface // https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens // Mainly has the following limitations: // 1. Need to submit token name and expires // 2. Optional to submit description, allowScopes, allowPackages information // 3. Need to implement ensureCurrentUser method in AuthAdapter, or pass in this.user async createGranularToken(ctx, tokenOptions) { ctx.tValidate(GranularTokenOptionsRule, tokenOptions); const user = await this.ensureWebUser(ctx.ip); // 生成 Token const { name, description, allowedPackages, allowedScopes, cidr_whitelist, automation, readonly, expires } = tokenOptions; const token = await this.userService.createToken(user.userId, { name, type: TokenType.granular, description, allowedPackages, allowedScopes, isAutomation: automation, isReadonly: readonly, cidrWhitelist: cidr_whitelist, expires, }); return { name: token.name, token: token.token, key: token.tokenKey, cidr_whitelist: token.cidrWhitelist, readonly: token.isReadonly, automation: token.isAutomation, allowedPackages: token.allowedPackages, allowedScopes: token.allowedScopes, created: token.createdAt, updated: token.updatedAt, }; } async listGranularTokens() { const user = await this.ensureWebUser(); const tokens = await this.userRepository.listTokens(user.userId); const granularTokens = tokens.filter((token) => isGranularToken(token)); const objects = granularTokens.map((token) => { const { name, description, expiredAt, allowedPackages, allowedScopes, lastUsedAt, type } = token; return { name, description, allowedPackages, allowedScopes, lastUsedAt, expiredAt, token: token.tokenMark, key: token.tokenKey, cidr_whitelist: token.cidrWhitelist, readonly: token.isReadonly, created: token.createdAt, updated: token.updatedAt, type, }; }); return { objects, total: granularTokens.length, urls: {} }; } async removeGranularToken(tokenKey) { const user = await this.ensureWebUser(); await this.userService.removeToken(user.userId, tokenKey); } }; __decorate([ Inject(), __metadata("design:type", Function) ], TokenController.prototype, "authAdapter", void 0); __decorate([ HTTPMethod({ path: '/-/npm/v1/tokens', method: HTTPMethodEnum.POST, }), __param(0, HTTPContext()), __param(1, HTTPBody()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context, Object]), __metadata("design:returntype", Promise) ], TokenController.prototype, "createToken", null); __decorate([ HTTPMethod({ path: '/-/npm/v1/tokens/token/:tokenKey', method: HTTPMethodEnum.DELETE, }), __param(0, HTTPContext()), __param(1, HTTPParam()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context, String]), __metadata("design:returntype", Promise) ], TokenController.prototype, "removeToken", null); __decorate([ HTTPMethod({ path: '/-/npm/v1/tokens', method: HTTPMethodEnum.GET, }), __param(0, HTTPContext()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context]), __metadata("design:returntype", Promise) ], TokenController.prototype, "listTokens", null); __decorate([ HTTPMethod({ path: '/-/npm/v1/tokens/gat', method: HTTPMethodEnum.POST, }) // Create granular access token through HTTP interface // https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens // Mainly has the following limitations: // 1. Need to submit token name and expires // 2. Optional to submit description, allowScopes, allowPackages information // 3. Need to implement ensureCurrentUser method in AuthAdapter, or pass in this.user , __param(0, HTTPContext()), __param(1, HTTPBody()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context, Object]), __metadata("design:returntype", Promise) ], TokenController.prototype, "createGranularToken", null); __decorate([ HTTPMethod({ path: '/-/npm/v1/tokens/gat', method: HTTPMethodEnum.GET, }), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], TokenController.prototype, "listGranularTokens", null); __decorate([ HTTPMethod({ path: '/-/npm/v1/tokens/gat/:tokenKey', method: HTTPMethodEnum.DELETE, }), __param(0, HTTPParam()), __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], TokenController.prototype, "removeGranularToken", null); TokenController = __decorate([ HTTPController() ], TokenController); export { TokenController }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVG9rZW5Db250cm9sbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vYXBwL3BvcnQvY29udHJvbGxlci9Ub2tlbkNvbnRyb2xsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQUEsT0FBTyxFQUFFLElBQUksRUFBZSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxXQUFXLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsVUFBVSxFQUFFLGNBQWMsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBQ3BILE9BQU8sRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFL0QsT0FBTyxFQUFFLFNBQVMsRUFBRSxlQUFlLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUV4RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUU3RCxxQ0FBcUM7QUFDckMsa0ZBQWtGO0FBRWxGLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNuQyxRQUFRLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBQ3ZELFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUN2QyxVQUFVLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDekMsMkJBQTJCO0lBQzNCLGNBQWMsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Q0FDN0YsQ0FBQyxDQUFDO0FBR0gsTUFBTSx3QkFBd0IsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO0lBQzNDLFVBQVUsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUN6QyxRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDdkMsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RixJQUFJLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQztJQUNyQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDM0QsYUFBYSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMzRixlQUFlLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzdGLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUM7Q0FDbkQsQ0FBQyxDQUFDO0FBSUksSUFBTSxlQUFlLEdBQXJCLE1BQU0sZUFBZ0IsU0FBUSxrQkFBa0I7SUFHckQsaUVBQWlFO0lBSzNELEFBQU4sS0FBSyxDQUFDLFdBQVcsQ0FBZ0IsR0FBWSxFQUFjLFlBQTBCO1FBQ25GLE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDekYsR0FBRyxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsRUFBRSxZQUFZLENBQUMsQ0FBQztRQUU5QyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsY0FBYyxFQUFFLFlBQVksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQzNFLE1BQU0sSUFBSSxpQkFBaUIsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBQ2xELENBQUM7UUFFRCxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUU7WUFDdEUsVUFBVSxFQUFFLFlBQVksQ0FBQyxRQUFRO1lBQ2pDLFlBQVksRUFBRSxZQUFZLENBQUMsVUFBVTtZQUNyQyxhQUFhLEVBQUUsWUFBWSxDQUFDLGNBQWM7U0FDM0MsQ0FBQyxDQUFDO1FBQ0gsT0FBTztZQUNMLEtBQUssRUFBRSxLQUFLLENBQUMsS0FBSztZQUNsQixHQUFHLEVBQUUsS0FBSyxDQUFDLFFBQVE7WUFDbkIsY0FBYyxFQUFFLEtBQUssQ0FBQyxhQUFhO1lBQ25DLFFBQVEsRUFBRSxLQUFLLENBQUMsVUFBVTtZQUMxQixVQUFVLEVBQUUsS0FBSyxDQUFDLFlBQVk7WUFDOUIsT0FBTyxFQUFFLEtBQUssQ0FBQyxTQUFTO1lBQ3hCLE9BQU8sRUFBRSxLQUFLLENBQUMsU0FBUztTQUN6QixDQUFDO0lBQ0osQ0FBQztJQUVELGlFQUFpRTtJQUszRCxBQUFOLEtBQUssQ0FBQyxXQUFXLENBQWdCLEdBQVksRUFBZSxRQUFnQjtRQUMxRSxNQUFNLGNBQWMsR0FBRyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsc0JBQXNCLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ3pGLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQztRQUNwRSxPQUFPLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDO0lBQ3RCLENBQUM7SUFFRCxpRUFBaUU7SUFLM0QsQUFBTixLQUFLLENBQUMsVUFBVSxDQUFnQixHQUFZO1FBQzFDLElBQUk7UUFDSiwyRUFBMkU7UUFDM0UsNEJBQTRCO1FBQzVCLHlDQUF5QztRQUN6QyxtQkFBbUI7UUFDbkIsdUNBQXVDO1FBQ3ZDLDRCQUE0QjtRQUM1Qiw2QkFBNkI7UUFDN0IsSUFBSTtRQUNKLE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDekYsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDM0UsSUFBSTtRQUNKLGlCQUFpQjtRQUNqQixRQUFRO1FBQ1IsMkJBQTJCO1FBQzNCLG1KQUFtSjtRQUNuSixnQ0FBZ0M7UUFDaEMsMkJBQTJCO1FBQzNCLDZCQUE2QjtRQUM3QiwrQ0FBK0M7UUFDL0MsOENBQThDO1FBQzlDLFFBQVE7UUFDUixPQUFPO1FBQ1AsZ0JBQWdCO1FBQ2hCLGVBQWU7UUFDZixJQUFJO1FBQ0osTUFBTSxPQUFPLEdBQUcsTUFBTTthQUNuQixNQUFNLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDO2FBQzFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztZQUNmLEtBQUssRUFBRSxLQUFLLENBQUMsU0FBUztZQUN0QixHQUFHLEVBQUUsS0FBSyxDQUFDLFFBQVE7WUFDbkIsY0FBYyxFQUFFLEtBQUssQ0FBQyxhQUFhO1lBQ25DLFFBQVEsRUFBRSxLQUFLLENBQUMsVUFBVTtZQUMxQixVQUFVLEVBQUUsS0FBSyxDQUFDLFlBQVk7WUFDOUIsT0FBTyxFQUFFLEtBQUssQ0FBQyxTQUFTO1lBQ3hCLFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVTtZQUM1QixPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVM7U0FDekIsQ0FBQyxDQUFDLENBQUM7UUFDTix1Q0FBdUM7UUFDdkMsT0FBTyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsT0FBTyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUM7SUFDdEQsQ0FBQztJQUVPLEtBQUssQ0FBQyxhQUFhLENBQUMsRUFBRSxHQUFHLEVBQUU7UUFDakMsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDM0QsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUM7WUFDdEMsTUFBTSxJQUFJLGNBQWMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFDRCxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsZ0JBQWdCLENBQUM7WUFDbkQsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO1lBQ2xCLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSztZQUNwQixFQUFFO1NBQ0gsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBWUssQUFOTixzREFBc0Q7SUFDdEQsMEVBQTBFO0lBQzFFLHdDQUF3QztJQUN4QywyQ0FBMkM7SUFDM0MsNEVBQTRFO0lBQzVFLHFGQUFxRjtJQUNyRixLQUFLLENBQUMsbUJBQW1CLENBQWdCLEdBQVksRUFBYyxZQUFrQztRQUNuRyxHQUFHLENBQUMsU0FBUyxDQUFDLHdCQUF3QixFQUFFLFlBQVksQ0FBQyxDQUFDO1FBQ3RELE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFOUMsV0FBVztRQUNYLE1BQU0sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLEdBQ3hHLFlBQVksQ0FBQztRQUNmLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUM1RCxJQUFJO1lBQ0osSUFBSSxFQUFFLFNBQVMsQ0FBQyxRQUFRO1lBQ3hCLFdBQVc7WUFDWCxlQUFlO1lBQ2YsYUFBYTtZQUNiLFlBQVksRUFBRSxVQUFVO1lBQ3hCLFVBQVUsRUFBRSxRQUFRO1lBQ3BCLGFBQWEsRUFBRSxjQUFjO1lBQzdCLE9BQU87U0FDUixDQUFDLENBQUM7UUFFSCxPQUFPO1lBQ0wsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO1lBQ2hCLEtBQUssRUFBRSxLQUFLLENBQUMsS0FBSztZQUNsQixHQUFHLEVBQUUsS0FBSyxDQUFDLFFBQVE7WUFDbkIsY0FBYyxFQUFFLEtBQUssQ0FBQyxhQUFhO1lBQ25DLFFBQVEsRUFBRSxLQUFLLENBQUMsVUFBVTtZQUMxQixVQUFVLEVBQUUsS0FBSyxDQUFDLFlBQVk7WUFDOUIsZUFBZSxFQUFFLEtBQUssQ0FBQyxlQUFlO1lBQ3RDLGFBQWEsRUFBRSxLQUFLLENBQUMsYUFBYTtZQUNsQyxPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVM7WUFDeEIsT0FBTyxFQUFFLEtBQUssQ0FBQyxTQUFTO1NBQ3pCLENBQUM7SUFDSixDQUFDO0lBTUssQUFBTixLQUFLLENBQUMsa0JBQWtCO1FBQ3RCLE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3hDLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2pFLE1BQU0sY0FBYyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBRXhFLE1BQU0sT0FBTyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUMzQyxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLEdBQUcsS0FBSyxDQUFDO1lBQ2pHLE9BQU87Z0JBQ0wsSUFBSTtnQkFDSixXQUFXO2dCQUNYLGVBQWU7Z0JBQ2YsYUFBYTtnQkFDYixVQUFVO2dCQUNWLFNBQVM7Z0JBQ1QsS0FBSyxFQUFFLEtBQUssQ0FBQyxTQUFTO2dCQUN0QixHQUFHLEVBQUUsS0FBSyxDQUFDLFFBQVE7Z0JBQ25CLGNBQWMsRUFBRSxLQUFLLENBQUMsYUFBYTtnQkFDbkMsUUFBUSxFQUFFLEtBQUssQ0FBQyxVQUFVO2dCQUMxQixPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVM7Z0JBQ3hCLE9BQU8sRUFBRSxLQUFLLENBQUMsU0FBUztnQkFDeEIsSUFBSTthQUNMLENBQUM7UUFDSixDQUFDLENBQUMsQ0FBQztRQUNILE9BQU8sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLGNBQWMsQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDO0lBQzdELENBQUM7SUFNSyxBQUFOLEtBQUssQ0FBQyxtQkFBbUIsQ0FBYyxRQUFnQjtRQUNyRCxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUN4QyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDNUQsQ0FBQztDQUNGLENBQUE7QUF2TGtCO0lBRGhCLE1BQU0sRUFBRTs7b0RBQ2lDO0FBTXBDO0lBSkwsVUFBVSxDQUFDO1FBQ1YsSUFBSSxFQUFFLGtCQUFrQjtRQUN4QixNQUFNLEVBQUUsY0FBYyxDQUFDLElBQUk7S0FDNUIsQ0FBQztJQUNpQixXQUFBLFdBQVcsRUFBRSxDQUFBO0lBQWdCLFdBQUEsUUFBUSxFQUFFLENBQUE7O3FDQUFwQixPQUFPOztrREFzQjVDO0FBT0s7SUFKTCxVQUFVLENBQUM7UUFDVixJQUFJLEVBQUUsa0NBQWtDO1FBQ3hDLE1BQU0sRUFBRSxjQUFjLENBQUMsTUFBTTtLQUM5QixDQUFDO0lBQ2lCLFdBQUEsV0FBVyxFQUFFLENBQUE7SUFBZ0IsV0FBQSxTQUFTLEVBQUUsQ0FBQTs7cUNBQXJCLE9BQU87O2tEQUk1QztBQU9LO0lBSkwsVUFBVSxDQUFDO1FBQ1YsSUFBSSxFQUFFLGtCQUFrQjtRQUN4QixNQUFNLEVBQUUsY0FBYyxDQUFDLEdBQUc7S0FDM0IsQ0FBQztJQUNnQixXQUFBLFdBQVcsRUFBRSxDQUFBOztxQ0FBTSxPQUFPOztpREF5QzNDO0FBeUJLO0lBVkwsVUFBVSxDQUFDO1FBQ1YsSUFBSSxFQUFFLHNCQUFzQjtRQUM1QixNQUFNLEVBQUUsY0FBYyxDQUFDLElBQUk7S0FDNUIsQ0FBQztJQUNGLHNEQUFzRDtJQUN0RCwwRUFBMEU7SUFDMUUsd0NBQXdDO0lBQ3hDLDJDQUEyQztJQUMzQyw0RUFBNEU7SUFDNUUscUZBQXFGOztJQUMxRCxXQUFBLFdBQVcsRUFBRSxDQUFBO0lBQWdCLFdBQUEsUUFBUSxFQUFFLENBQUE7O3FDQUFwQixPQUFPOzswREErQnBEO0FBTUs7SUFKTCxVQUFVLENBQUM7UUFDVixJQUFJLEVBQUUsc0JBQXNCO1FBQzVCLE1BQU0sRUFBRSxjQUFjLENBQUMsR0FBRztLQUMzQixDQUFDOzs7O3lEQXlCRDtBQU1LO0lBSkwsVUFBVSxDQUFDO1FBQ1YsSUFBSSxFQUFFLGdDQUFnQztRQUN0QyxNQUFNLEVBQUUsY0FBYyxDQUFDLE1BQU07S0FDOUIsQ0FBQztJQUN5QixXQUFBLFNBQVMsRUFBRSxDQUFBOzs7OzBEQUdyQztBQXhMVSxlQUFlO0lBRDNCLGNBQWMsRUFBRTtHQUNKLGVBQWUsQ0F5TDNCIn0=