cnpmcore
Version:
263 lines • 21.3 kB
JavaScript
;
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); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenController = void 0;
const egg_errors_1 = require("egg-errors");
const AuthAdapter_1 = require("../../infra/AuthAdapter");
const tegg_1 = require("@eggjs/tegg");
const typebox_1 = require("@sinclair/typebox");
const AbstractController_1 = require("./AbstractController");
const Token_1 = require("../../core/entity/Token");
// Creating and viewing access tokens
// https://docs.npmjs.com/creating-and-viewing-access-tokens#viewing-access-tokens
const TokenOptionsRule = typebox_1.Type.Object({
password: typebox_1.Type.String({ minLength: 8, maxLength: 100 }),
readonly: typebox_1.Type.Optional(typebox_1.Type.Boolean()),
automation: typebox_1.Type.Optional(typebox_1.Type.Boolean()),
// only allow 10 ip for now
cidr_whitelist: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 100 }), { maxItems: 10 })),
});
const GranularTokenOptionsRule = typebox_1.Type.Object({
automation: typebox_1.Type.Optional(typebox_1.Type.Boolean()),
readonly: typebox_1.Type.Optional(typebox_1.Type.Boolean()),
cidr_whitelist: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 100 }), { maxItems: 10 })),
name: typebox_1.Type.String({ maxLength: 255 }),
description: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 255 })),
allowedScopes: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 100 }), { maxItems: 50 })),
allowedPackages: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 100 }), { maxItems: 50 })),
expires: typebox_1.Type.Number({ minimum: 1, maximum: 365 }),
});
let TokenController = class TokenController extends AbstractController_1.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 egg_errors_1.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 => !(0, Token_1.isGranularToken)(token))
.map(token => {
return {
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 egg_errors_1.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: Token_1.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 => (0, Token_1.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);
}
};
exports.TokenController = TokenController;
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", AuthAdapter_1.AuthAdapter)
], TokenController.prototype, "authAdapter", void 0);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/tokens',
method: tegg_1.HTTPMethodEnum.POST,
}),
__param(0, (0, tegg_1.Context)()),
__param(1, (0, tegg_1.HTTPBody)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], TokenController.prototype, "createToken", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/tokens/token/:tokenKey',
method: tegg_1.HTTPMethodEnum.DELETE,
}),
__param(0, (0, tegg_1.Context)()),
__param(1, (0, tegg_1.HTTPParam)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], TokenController.prototype, "removeToken", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/tokens',
method: tegg_1.HTTPMethodEnum.GET,
}),
__param(0, (0, tegg_1.Context)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], TokenController.prototype, "listTokens", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/tokens/gat',
method: tegg_1.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, (0, tegg_1.Context)()),
__param(1, (0, tegg_1.HTTPBody)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], TokenController.prototype, "createGranularToken", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/tokens/gat',
method: tegg_1.HTTPMethodEnum.GET,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], TokenController.prototype, "listGranularTokens", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/tokens/gat/:tokenKey',
method: tegg_1.HTTPMethodEnum.DELETE,
}),
__param(0, (0, tegg_1.HTTPParam)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], TokenController.prototype, "removeGranularToken", null);
exports.TokenController = TokenController = __decorate([
(0, tegg_1.HTTPController)()
], TokenController);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVG9rZW5Db250cm9sbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vYXBwL3BvcnQvY29udHJvbGxlci9Ub2tlbkNvbnRyb2xsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsMkNBQStEO0FBQy9ELHlEQUFzRDtBQUN0RCxzQ0FTcUI7QUFDckIsK0NBQWlEO0FBQ2pELDZEQUEwRDtBQUMxRCxtREFBcUU7QUFFckUscUNBQXFDO0FBQ3JDLGtGQUFrRjtBQUVsRixNQUFNLGdCQUFnQixHQUFHLGNBQUksQ0FBQyxNQUFNLENBQUM7SUFDbkMsUUFBUSxFQUFFLGNBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQztJQUN2RCxRQUFRLEVBQUUsY0FBSSxDQUFDLFFBQVEsQ0FBQyxjQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDdkMsVUFBVSxFQUFFLGNBQUksQ0FBQyxRQUFRLENBQUMsY0FBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ3pDLDJCQUEyQjtJQUMzQixjQUFjLEVBQUUsY0FBSSxDQUFDLFFBQVEsQ0FBQyxjQUFJLENBQUMsS0FBSyxDQUFDLGNBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0NBQzdGLENBQUMsQ0FBQztBQUdILE1BQU0sd0JBQXdCLEdBQUcsY0FBSSxDQUFDLE1BQU0sQ0FBQztJQUMzQyxVQUFVLEVBQUUsY0FBSSxDQUFDLFFBQVEsQ0FBQyxjQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDekMsUUFBUSxFQUFFLGNBQUksQ0FBQyxRQUFRLENBQUMsY0FBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ3ZDLGNBQWMsRUFBRSxjQUFJLENBQUMsUUFBUSxDQUFDLGNBQUksQ0FBQyxLQUFLLENBQUMsY0FBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDNUYsSUFBSSxFQUFFLGNBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLENBQUM7SUFDckMsV0FBVyxFQUFFLGNBQUksQ0FBQyxRQUFRLENBQUMsY0FBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQzNELGFBQWEsRUFBRSxjQUFJLENBQUMsUUFBUSxDQUFDLGNBQUksQ0FBQyxLQUFLLENBQUMsY0FBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDM0YsZUFBZSxFQUFFLGNBQUksQ0FBQyxRQUFRLENBQUMsY0FBSSxDQUFDLEtBQUssQ0FBQyxjQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM3RixPQUFPLEVBQUUsY0FBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDO0NBQ25ELENBQUMsQ0FBQztBQUlJLElBQU0sZUFBZSxHQUFyQixNQUFNLGVBQWdCLFNBQVEsdUNBQWtCO0lBR3JELGlFQUFpRTtJQUszRCxBQUFOLEtBQUssQ0FBQyxXQUFXLENBQVksR0FBZSxFQUFjLFlBQTBCO1FBQ2xGLE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDekYsR0FBRyxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsRUFBRSxZQUFZLENBQUMsQ0FBQztRQUU5QyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsY0FBYyxFQUFFLFlBQVksQ0FBQyxRQUFRLENBQUMsRUFBRTtZQUMxRSxNQUFNLElBQUksOEJBQWlCLENBQUMsa0JBQWtCLENBQUMsQ0FBQztTQUNqRDtRQUVELE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLE1BQU0sRUFBRTtZQUN0RSxVQUFVLEVBQUUsWUFBWSxDQUFDLFFBQVE7WUFDakMsWUFBWSxFQUFFLFlBQVksQ0FBQyxVQUFVO1lBQ3JDLGFBQWEsRUFBRSxZQUFZLENBQUMsY0FBYztTQUMzQyxDQUFDLENBQUM7UUFDSCxPQUFPO1lBQ0wsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLO1lBQ2xCLEdBQUcsRUFBRSxLQUFLLENBQUMsUUFBUTtZQUNuQixjQUFjLEVBQUUsS0FBSyxDQUFDLGFBQWE7WUFDbkMsUUFBUSxFQUFFLEtBQUssQ0FBQyxVQUFVO1lBQzFCLFVBQVUsRUFBRSxLQUFLLENBQUMsWUFBWTtZQUM5QixPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVM7WUFDeEIsT0FBTyxFQUFFLEtBQUssQ0FBQyxTQUFTO1NBQ3pCLENBQUM7SUFDSixDQUFDO0lBRUQsaUVBQWlFO0lBSzNELEFBQU4sS0FBSyxDQUFDLFdBQVcsQ0FBWSxHQUFlLEVBQWUsUUFBZ0I7UUFDekUsTUFBTSxjQUFjLEdBQUcsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLHNCQUFzQixDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsQ0FBQztRQUN6RixNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDcEUsT0FBTyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRUQsaUVBQWlFO0lBSzNELEFBQU4sS0FBSyxDQUFDLFVBQVUsQ0FBWSxHQUFlO1FBQ3pDLElBQUk7UUFDSiwyRUFBMkU7UUFDM0UsNEJBQTRCO1FBQzVCLHlDQUF5QztRQUN6QyxtQkFBbUI7UUFDbkIsdUNBQXVDO1FBQ3ZDLDRCQUE0QjtRQUM1Qiw2QkFBNkI7UUFDN0IsSUFBSTtRQUNKLE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDekYsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDM0UsSUFBSTtRQUNKLGlCQUFpQjtRQUNqQixRQUFRO1FBQ1IsMkJBQTJCO1FBQzNCLG1KQUFtSjtRQUNuSixnQ0FBZ0M7UUFDaEMsMkJBQTJCO1FBQzNCLDZCQUE2QjtRQUM3QiwrQ0FBK0M7UUFDL0MsOENBQThDO1FBQzlDLFFBQVE7UUFDUixPQUFPO1FBQ1AsZ0JBQWdCO1FBQ2hCLGVBQWU7UUFDZixJQUFJO1FBQ0osTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBQSx1QkFBZSxFQUFDLEtBQUssQ0FBQyxDQUFDO2FBQzVELEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRTtZQUNYLE9BQU87Z0JBQ0wsS0FBSyxFQUFFLEtBQUssQ0FBQyxTQUFTO2dCQUN0QixHQUFHLEVBQUUsS0FBSyxDQUFDLFFBQVE7Z0JBQ25CLGNBQWMsRUFBRSxLQUFLLENBQUMsYUFBYTtnQkFDbkMsUUFBUSxFQUFFLEtBQUssQ0FBQyxVQUFVO2dCQUMxQixVQUFVLEVBQUUsS0FBSyxDQUFDLFlBQVk7Z0JBQzlCLE9BQU8sRUFBRSxLQUFLLENBQUMsU0FBUztnQkFDeEIsVUFBVSxFQUFFLEtBQUssQ0FBQyxVQUFVO2dCQUM1QixPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVM7YUFDekIsQ0FBQztRQUNKLENBQUMsQ0FBQyxDQUFDO1FBQ0wsdUNBQXVDO1FBQ3ZDLE9BQU8sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDO0lBQ3RELENBQUM7SUFFTyxLQUFLLENBQUMsYUFBYSxDQUFDLEVBQUUsR0FBRyxFQUFFO1FBQ2pDLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBQzNELElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLEtBQUssRUFBRTtZQUNyQyxNQUFNLElBQUksMkJBQWMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1NBQzlDO1FBQ0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLGdCQUFnQixDQUFDLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUN2RyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFZSyxBQU5OLHNEQUFzRDtJQUN0RCwwRUFBMEU7SUFDMUUsd0NBQXdDO0lBQ3hDLDJDQUEyQztJQUMzQyw0RUFBNEU7SUFDNUUscUZBQXFGO0lBQ3JGLEtBQUssQ0FBQyxtQkFBbUIsQ0FBWSxHQUFlLEVBQWMsWUFBa0M7UUFDbEcsR0FBRyxDQUFDLFNBQVMsQ0FBQyx3QkFBd0IsRUFBRSxZQUFZLENBQUMsQ0FBQztRQUN0RCxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBRTlDLFdBQVc7UUFDWCxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxlQUFlLEVBQUUsYUFBYSxFQUFFLGNBQWMsRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxHQUFHLFlBQVksQ0FBQztRQUMxSCxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUU7WUFDNUQsSUFBSTtZQUNKLElBQUksRUFBRSxpQkFBUyxDQUFDLFFBQVE7WUFDeEIsV0FBVztZQUNYLGVBQWU7WUFDZixhQUFhO1lBQ2IsWUFBWSxFQUFFLFVBQVU7WUFDeEIsVUFBVSxFQUFFLFFBQVE7WUFDcEIsYUFBYSxFQUFFLGNBQWM7WUFDN0IsT0FBTztTQUNSLENBQUMsQ0FBQztRQUVILE9BQU87WUFDTCxJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7WUFDaEIsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLO1lBQ2xCLEdBQUcsRUFBRSxLQUFLLENBQUMsUUFBUTtZQUNuQixjQUFjLEVBQUUsS0FBSyxDQUFDLGFBQWE7WUFDbkMsUUFBUSxFQUFFLEtBQUssQ0FBQyxVQUFVO1lBQzFCLFVBQVUsRUFBRSxLQUFLLENBQUMsWUFBWTtZQUM5QixlQUFlLEVBQUUsS0FBSyxDQUFDLGVBQWU7WUFDdEMsYUFBYSxFQUFFLEtBQUssQ0FBQyxhQUFhO1lBQ2xDLE9BQU8sRUFBRSxLQUFLLENBQUMsU0FBUztZQUN4QixPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVM7U0FDekIsQ0FBQztJQUNKLENBQUM7SUFNSyxBQUFOLEtBQUssQ0FBQyxrQkFBa0I7UUFDdEIsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDeEMsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDakUsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLElBQUEsdUJBQWUsRUFBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBRXRFLE1BQU0sT0FBTyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUU7WUFDekMsTUFBTSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQztZQUNqRyxPQUFPO2dCQUNMLElBQUk7Z0JBQ0osV0FBVztnQkFDWCxlQUFlO2dCQUNmLGFBQWE7Z0JBQ2IsVUFBVTtnQkFDVixTQUFTO2dCQUNULEtBQUssRUFBRSxLQUFLLENBQUMsU0FBUztnQkFDdEIsR0FBRyxFQUFFLEtBQUssQ0FBQyxRQUFRO2dCQUNuQixjQUFjLEVBQUUsS0FBSyxDQUFDLGFBQWE7Z0JBQ25DLFFBQVEsRUFBRSxLQUFLLENBQUMsVUFBVTtnQkFDMUIsT0FBTyxFQUFFLEtBQUssQ0FBQyxTQUFTO2dCQUN4QixPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVM7Z0JBQ3hCLElBQUk7YUFDTCxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7UUFDSCxPQUFPLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxjQUFjLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsQ0FBQztJQUM3RCxDQUFDO0lBTUssQUFBTixLQUFLLENBQUMsbUJBQW1CLENBQWMsUUFBZ0I7UUFDckQsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDeEMsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQzVELENBQUM7Q0FDRixDQUFBO0FBckxZLDBDQUFlO0FBRVQ7SUFEaEIsSUFBQSxhQUFNLEdBQUU7OEJBQ3FCLHlCQUFXO29EQUFDO0FBTXBDO0lBSkwsSUFBQSxpQkFBVSxFQUFDO1FBQ1YsSUFBSSxFQUFFLGtCQUFrQjtRQUN4QixNQUFNLEVBQUUscUJBQWMsQ0FBQyxJQUFJO0tBQzVCLENBQUM7SUFDaUIsV0FBQSxJQUFBLGNBQU8sR0FBRSxDQUFBO0lBQW1CLFdBQUEsSUFBQSxlQUFRLEdBQUUsQ0FBQTs7OztrREFzQnhEO0FBT0s7SUFKTCxJQUFBLGlCQUFVLEVBQUM7UUFDVixJQUFJLEVBQUUsa0NBQWtDO1FBQ3hDLE1BQU0sRUFBRSxxQkFBYyxDQUFDLE1BQU07S0FDOUIsQ0FBQztJQUNpQixXQUFBLElBQUEsY0FBTyxHQUFFLENBQUE7SUFBbUIsV0FBQSxJQUFBLGdCQUFTLEdBQUUsQ0FBQTs7OztrREFJekQ7QUFPSztJQUpMLElBQUEsaUJBQVUsRUFBQztRQUNWLElBQUksRUFBRSxrQkFBa0I7UUFDeEIsTUFBTSxFQUFFLHFCQUFjLENBQUMsR0FBRztLQUMzQixDQUFDO0lBQ2dCLFdBQUEsSUFBQSxjQUFPLEdBQUUsQ0FBQTs7OztpREEwQzFCO0FBcUJLO0lBVkwsSUFBQSxpQkFBVSxFQUFDO1FBQ1YsSUFBSSxFQUFFLHNCQUFzQjtRQUM1QixNQUFNLEVBQUUscUJBQWMsQ0FBQyxJQUFJO0tBQzVCLENBQUM7SUFDRixzREFBc0Q7SUFDdEQsMEVBQTBFO0lBQzFFLHdDQUF3QztJQUN4QywyQ0FBMkM7SUFDM0MsNEVBQTRFO0lBQzVFLHFGQUFxRjs7SUFDMUQsV0FBQSxJQUFBLGNBQU8sR0FBRSxDQUFBO0lBQW1CLFdBQUEsSUFBQSxlQUFRLEdBQUUsQ0FBQTs7OzswREE4QmhFO0FBTUs7SUFKTCxJQUFBLGlCQUFVLEVBQUM7UUFDVixJQUFJLEVBQUUsc0JBQXNCO1FBQzVCLE1BQU0sRUFBRSxxQkFBYyxDQUFDLEdBQUc7S0FDM0IsQ0FBQzs7Ozt5REF5QkQ7QUFNSztJQUpMLElBQUEsaUJBQVUsRUFBQztRQUNWLElBQUksRUFBRSxnQ0FBZ0M7UUFDdEMsTUFBTSxFQUFFLHFCQUFjLENBQUMsTUFBTTtLQUM5QixDQUFDO0lBQ3lCLFdBQUEsSUFBQSxnQkFBUyxHQUFFLENBQUE7Ozs7MERBR3JDOzBCQXBMVSxlQUFlO0lBRDNCLElBQUEscUJBQWMsR0FBRTtHQUNKLGVBQWUsQ0FxTDNCIn0=