@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
190 lines ⢠24.8 kB
JavaScript
/**
* IdentityHandler - Manages user identity and attribution
*
* Handles user identity management for persona attribution including:
* - Setting user identity (username and optional email)
* - Retrieving current identity status
* - Clearing identity to return to anonymous mode
* - Providing attribution for new personas
*
* Identity state now flows through PersonaManager for DI integration.
*/
import { validateUsername, sanitizeInput } from '../security/InputValidator.js';
import { VALIDATION_PATTERNS } from '../security/constants.js';
import { SecureErrorHandler } from '../security/errorHandler.js';
import { SecurityMonitor } from '../security/securityMonitor.js';
export class IdentityHandler {
personaManager;
initService;
indicatorService;
constructor(personaManager, initService, indicatorService) {
this.personaManager = personaManager;
this.initService = initService;
this.indicatorService = indicatorService;
}
async ensureInitialized() {
await this.initService.ensureInitialized();
}
withIndicator(message) {
const indicator = this.indicatorService.getPersonaIndicator();
return `${indicator}${message}`;
}
/**
* Set user identity for attribution
* @param username - Username to set (alphanumeric, hyphens, underscores, dots)
* @param email - Optional email address
*/
async setUserIdentity(username, email) {
await this.ensureInitialized();
try {
if (!username || username.trim().length === 0) {
return {
content: [
{
type: 'text',
text: this.withIndicator('ā Username cannot be empty'),
},
],
};
}
const validatedUsername = validateUsername(username);
let validatedEmail;
if (email) {
const sanitizedEmail = sanitizeInput(email, 100);
if (!VALIDATION_PATTERNS.SAFE_EMAIL.test(sanitizedEmail)) {
throw new Error('Invalid email format');
}
validatedEmail = sanitizedEmail;
}
this.personaManager.setUserIdentity(validatedUsername, validatedEmail);
const { username: currentUsername, email: currentEmail } = this.personaManager.getUserIdentity();
// FIX: DMCP-SEC-006 - Add security audit logging for identity changes
SecurityMonitor.logSecurityEvent({
type: 'IDENTITY_CHANGED',
severity: 'LOW',
source: 'IdentityHandler.setUserIdentity',
details: `User identity set: ${currentUsername}`,
additionalData: {
username: currentUsername,
emailProvided: !!currentEmail
}
});
return {
content: [
{
type: 'text',
text: this.withIndicator(`ā
**User Identity Set**\n\n` +
`š¤ **Username:** ${currentUsername}\n` +
`${currentEmail ? `š§ **Email:** ${currentEmail}\n` : ''}` +
`\nšÆ **Next Steps:**\n` +
`⢠New personas you create will be attributed to "${currentUsername}"\n` +
`⢠Set environment variable \`DOLLHOUSE_USER=${currentUsername}\` to persist this setting\n` +
`${currentEmail ? `⢠Set environment variable \`DOLLHOUSE_EMAIL=${currentEmail}\` for contact info\n` : ''}` +
`⢠Use \`clear_user_identity\` to return to anonymous mode`),
},
],
};
}
catch (error) {
const sanitized = SecureErrorHandler.sanitizeError(error);
return {
content: [
{
type: 'text',
text: this.withIndicator('ā **Validation Error**\n\n') +
`${sanitized.message}\n\n` +
`Please provide a valid username (alphanumeric characters, hyphens, underscores, dots only).`,
},
],
};
}
}
/**
* Get current user identity status
* Shows username, email (if set), and attribution status
*/
async getUserIdentity() {
await this.ensureInitialized();
const { username: currentUsername, email } = this.personaManager.getUserIdentity();
if (!currentUsername) {
return {
content: [
{
type: 'text',
text: this.withIndicator(`š¤ **User Identity: Anonymous**\n\n` +
`š **Status:** Anonymous mode\n` +
`š **Attribution:** Personas will use anonymous IDs\n\n` +
`**To set your identity:**\n` +
`⢠Use: \`set_user_identity "your-username"\`\n` +
`⢠Or set environment variable: \`DOLLHOUSE_USER=your-username\``),
},
],
};
}
return {
content: [
{
type: 'text',
text: this.withIndicator(`š¤ **User Identity: ${currentUsername}**\n\n` +
`ā
**Status:** Authenticated\n` +
`š¤ **Username:** ${currentUsername}\n` +
`${email ? `š§ **Email:** ${email}\n` : ''}` +
`š **Attribution:** New personas will be credited to "${currentUsername}"\n\n` +
`**Environment Variables:**\n` +
`⢠\`DOLLHOUSE_USER=${currentUsername}\`\n` +
`${email ? `⢠\`DOLLHOUSE_EMAIL=${email}\`\n` : ''}` +
`\n**Management:**\n` +
`⢠Use \`clear_user_identity\` to return to anonymous mode\n` +
`⢠Use \`set_user_identity "new-username"\` to change username`),
},
],
};
}
/**
* Clear user identity and return to anonymous mode
*/
async clearUserIdentity() {
await this.ensureInitialized();
const { username: currentUsername } = this.personaManager.getUserIdentity();
const wasSet = currentUsername !== null;
const previousUser = currentUsername;
this.personaManager.clearUserIdentity();
// FIX: DMCP-SEC-006 - Add security audit logging for identity clearing
if (wasSet) {
SecurityMonitor.logSecurityEvent({
type: 'ELEMENT_DELETED',
severity: 'LOW',
source: 'IdentityHandler.clearUserIdentity',
details: `User identity cleared: previous=${previousUser}`,
additionalData: {
previousUsername: previousUser
}
});
}
return {
content: [
{
type: 'text',
text: wasSet
? this.withIndicator(`ā
**User Identity Cleared**\n\n` +
`š¤ **Previous:** ${previousUser}\n` +
`š **Current:** Anonymous mode\n\n` +
`š **Effect:** New personas will use anonymous IDs\n\n` +
`ā ļø **Note:** This only affects the current session.\n` +
`To persist this change, unset the \`DOLLHOUSE_USER\` environment variable.`)
: this.withIndicator(`ā¹ļø **Already in Anonymous Mode**\n\n` +
`š¤ No user identity was set.\n\n` +
`Use \`set_user_identity "username"\` to set your identity.`),
},
],
};
}
/**
* Get current user for attribution purposes
* Returns username if set, otherwise PersonaManager's anonymous ID
*/
getCurrentUserForAttribution() {
return this.personaManager.getCurrentUserForAttribution();
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSWRlbnRpdHlIYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hhbmRsZXJzL0lkZW50aXR5SGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7OztHQVVHO0FBRUgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLGFBQWEsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ2hGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQy9ELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBSWpFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUVqRSxNQUFNLE9BQU8sZUFBZTtJQUVQO0lBQ0E7SUFDQTtJQUhuQixZQUNtQixjQUE4QixFQUM5QixXQUFrQyxFQUNsQyxnQkFBeUM7UUFGekMsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQzlCLGdCQUFXLEdBQVgsV0FBVyxDQUF1QjtRQUNsQyxxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQXlCO0lBQ3pELENBQUM7SUFFSSxLQUFLLENBQUMsaUJBQWlCO1FBQzdCLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO0lBQzdDLENBQUM7SUFFTyxhQUFhLENBQUMsT0FBZTtRQUNuQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUM5RCxPQUFPLEdBQUcsU0FBUyxHQUFHLE9BQU8sRUFBRSxDQUFDO0lBQ2xDLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLGVBQWUsQ0FBQyxRQUFnQixFQUFFLEtBQWM7UUFDcEQsTUFBTSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztRQUUvQixJQUFJLENBQUM7WUFDSCxJQUFJLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEtBQUssQ0FB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O2dCQUN2QixRQUFRLEVBQUUsS0FBSztnQkFDZixNQUFNLEVBQUUsbUNBQW1DO2dCQUMzQyxPQUFPLEVBQUUsbUNBQW1DLFlBQVksRUFBRTtnQkFDMUQsY0FBYyxFQUFFO29CQUNkLGdCQUFnQixFQUFFLFlBQVk7aUJBQy9CO2FBQ0YsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELE9BQU87WUFDTCxPQUFPLEVBQUU7Z0JBQ1A7b0JBQ0UsSUFBSSxFQUFFLE1BQWU7b0JBQ3JCLElBQUksRUFBRSxNQUFNO3dCQUNWLENBQUMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUNoQixpQ0FBaUM7NEJBQy9CLG9CQUFvQixZQUFZLElBQUk7NEJBQ3BDLG9DQUFvQzs0QkFDcEMsd0RBQXdEOzRCQUN4RCx1REFBdUQ7NEJBQ3ZELDRFQUE0RSxDQUMvRTt3QkFDSCxDQUFDLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FDaEIsc0NBQXNDOzRCQUNwQyxrQ0FBa0M7NEJBQ2xDLDREQUE0RCxDQUMvRDtpQkFDTjthQUNGO1NBQ0YsQ0FBQztJQUNKLENBQUM7SUFFRDs7O09BR0c7SUFDSSw0QkFBNEI7UUFDakMsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLDRCQUE0QixFQUFFLENBQUM7SUFDNUQsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBJZGVudGl0eUhhbmRsZXIgLSBNYW5hZ2VzIHVzZXIgaWRlbnRpdHkgYW5kIGF0dHJpYnV0aW9uXG4gKlxuICogSGFuZGxlcyB1c2VyIGlkZW50aXR5IG1hbmFnZW1lbnQgZm9yIHBlcnNvbmEgYXR0cmlidXRpb24gaW5jbHVkaW5nOlxuICogLSBTZXR0aW5nIHVzZXIgaWRlbnRpdHkgKHVzZXJuYW1lIGFuZCBvcHRpb25hbCBlbWFpbClcbiAqIC0gUmV0cmlldmluZyBjdXJyZW50IGlkZW50aXR5IHN0YXR1c1xuICogLSBDbGVhcmluZyBpZGVudGl0eSB0byByZXR1cm4gdG8gYW5vbnltb3VzIG1vZGVcbiAqIC0gUHJvdmlkaW5nIGF0dHJpYnV0aW9uIGZvciBuZXcgcGVyc29uYXNcbiAqXG4gKiBJZGVudGl0eSBzdGF0ZSBub3cgZmxvd3MgdGhyb3VnaCBQZXJzb25hTWFuYWdlciBmb3IgREkgaW50ZWdyYXRpb24uXG4gKi9cblxuaW1wb3J0IHsgdmFsaWRhdGVVc2VybmFtZSwgc2FuaXRpemVJbnB1dCB9IGZyb20gJy4uL3NlY3VyaXR5L0lucHV0VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFZBTElEQVRJT05fUEFUVEVSTlMgfSBmcm9tICcuLi9zZWN1cml0eS9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgU2VjdXJlRXJyb3JIYW5kbGVyIH0gZnJvbSAnLi4vc2VjdXJpdHkvZXJyb3JIYW5kbGVyLmpzJztcbmltcG9ydCB7IFBlcnNvbmFNYW5hZ2VyIH0gZnJvbSAnLi4vcGVyc29uYS9QZXJzb25hTWFuYWdlci5qcyc7XG5pbXBvcnQgeyBJbml0aWFsaXphdGlvblNlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy9Jbml0aWFsaXphdGlvblNlcnZpY2UuanMnO1xuaW1wb3J0IHsgUGVyc29uYUluZGljYXRvclNlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy9QZXJzb25hSW5kaWNhdG9yU2VydmljZS5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuXG5leHBvcnQgY2xhc3MgSWRlbnRpdHlIYW5kbGVyIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBwZXJzb25hTWFuYWdlcjogUGVyc29uYU1hbmFnZXIsXG4gICAgcHJpdmF0ZSByZWFkb25seSBpbml0U2VydmljZTogSW5pdGlhbGl6YXRpb25TZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgaW5kaWNhdG9yU2VydmljZTogUGVyc29uYUluZGljYXRvclNlcnZpY2VcbiAgKSB7fVxuXG4gIHByaXZhdGUgYXN5bmMgZW5zdXJlSW5pdGlhbGl6ZWQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5pbml0U2VydmljZS5lbnN1cmVJbml0aWFsaXplZCgpO1xuICB9XG5cbiAgcHJpdmF0ZSB3aXRoSW5kaWNhdG9yKG1lc3NhZ2U6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgaW5kaWNhdG9yID0gdGhpcy5pbmRpY2F0b3JTZXJ2aWNlLmdldFBlcnNvbmFJbmRpY2F0b3IoKTtcbiAgICByZXR1cm4gYCR7aW5kaWNhdG9yfSR7bWVzc2FnZX1gO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCB1c2VyIGlkZW50aXR5IGZvciBhdHRyaWJ1dGlvblxuICAgKiBAcGFyYW0gdXNlcm5hbWUgLSBVc2VybmFtZSB0byBzZXQgKGFscGhhbnVtZXJpYywgaHlwaGVucywgdW5kZXJzY29yZXMsIGRvdHMpXG4gICAqIEBwYXJhbSBlbWFpbCAtIE9wdGlvbmFsIGVtYWlsIGFkZHJlc3NcbiAgICovXG4gIGFzeW5jIHNldFVzZXJJZGVudGl0eSh1c2VybmFtZTogc3RyaW5nLCBlbWFpbD86IHN0cmluZykge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlSW5pdGlhbGl6ZWQoKTtcblxuICAgIHRyeSB7XG4gICAgICBpZiAoIXVzZXJuYW1lIHx8IHVzZXJuYW1lLnRyaW0oKS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBjb250ZW50OiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHR5cGU6ICd0ZXh0JyBhcyBjb25zdCxcbiAgICAgICAgICAgICAgdGV4dDogdGhpcy53aXRoSW5kaWNhdG9yKCfinYwgVXNlcm5hbWUgY2Fubm90IGJlIGVtcHR5JyksXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIF0sXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHZhbGlkYXRlZFVzZXJuYW1lID0gdmFsaWRhdGVVc2VybmFtZSh1c2VybmFtZSk7XG5cbiAgICAgIGxldCB2YWxpZGF0ZWRFbWFpbDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgICAgaWYgKGVtYWlsKSB7XG4gICAgICAgIGNvbnN0IHNhbml0aXplZEVtYWlsID0gc2FuaXRpemVJbnB1dChlbWFpbCwgMTAwKTtcbiAgICAgICAgaWYgKCFWQUxJREFUSU9OX1BBVFRFUk5TLlNBRkVfRU1BSUwudGVzdChzYW5pdGl6ZWRFbWFpbCkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZW1haWwgZm9ybWF0Jyk7XG4gICAgICAgIH1cbiAgICAgICAgdmFsaWRhdGVkRW1haWwgPSBzYW5pdGl6ZWRFbWFpbDtcbiAgICAgIH1cblxuICAgICAgdGhpcy5wZXJzb25hTWFuYWdlci5zZXRVc2VySWRlbnRpdHkodmFsaWRhdGVkVXNlcm5hbWUsIHZhbGlkYXRlZEVtYWlsKTtcbiAgICAgIGNvbnN0IHsgdXNlcm5hbWU6IGN1cnJlbnRVc2VybmFtZSwgZW1haWw6IGN1cnJlbnRFbWFpbCB9ID0gdGhpcy5wZXJzb25hTWFuYWdlci5nZXRVc2VySWRlbnRpdHkoKTtcblxuICAgICAgLy8gRklYOiBETUNQLVNFQy0wMDYgLSBBZGQgc2VjdXJpdHkgYXVkaXQgbG9nZ2luZyBmb3IgaWRlbnRpdHkgY2hhbmdlc1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnSURFTlRJVFlfQ0hBTkdFRCcsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnSWRlbnRpdHlIYW5kbGVyLnNldFVzZXJJZGVudGl0eScsXG4gICAgICAgIGRldGFpbHM6IGBVc2VyIGlkZW50aXR5IHNldDogJHtjdXJyZW50VXNlcm5hbWV9YCxcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IHtcbiAgICAgICAgICB1c2VybmFtZTogY3VycmVudFVzZXJuYW1lLFxuICAgICAgICAgIGVtYWlsUHJvdmlkZWQ6ICEhY3VycmVudEVtYWlsXG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBjb250ZW50OiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgdHlwZTogJ3RleHQnIGFzIGNvbnN0LFxuICAgICAgICAgICAgdGV4dDogdGhpcy53aXRoSW5kaWNhdG9yKFxuICAgICAgICAgICAgICBg4pyFICoqVXNlciBJZGVudGl0eSBTZXQqKlxcblxcbmAgK1xuICAgICAgICAgICAgICAgIGDwn5GkICoqVXNlcm5hbWU6KiogJHtjdXJyZW50VXNlcm5hbWV9XFxuYCArXG4gICAgICAgICAgICAgICAgYCR7Y3VycmVudEVtYWlsID8gYPCfk6cgKipFbWFpbDoqKiAke2N1cnJlbnRFbWFpbH1cXG5gIDogJyd9YCArXG4gICAgICAgICAgICAgICAgYFxcbvCfjq8gKipOZXh0IFN0ZXBzOioqXFxuYCArXG4gICAgICAgICAgICAgICAgYOKAoiBOZXcgcGVyc29uYXMgeW91IGNyZWF0ZSB3aWxsIGJlIGF0dHJpYnV0ZWQgdG8gXCIke2N1cnJlbnRVc2VybmFtZX1cIlxcbmAgK1xuICAgICAgICAgICAgICAgIGDigKIgU2V0IGVudmlyb25tZW50IHZhcmlhYmxlIFxcYERPTExIT1VTRV9VU0VSPSR7Y3VycmVudFVzZXJuYW1lfVxcYCB0byBwZXJzaXN0IHRoaXMgc2V0dGluZ1xcbmAgK1xuICAgICAgICAgICAgICAgIGAke2N1cnJlbnRFbWFpbCA/IGDigKIgU2V0IGVudmlyb25tZW50IHZhcmlhYmxlIFxcYERPTExIT1VTRV9FTUFJTD0ke2N1cnJlbnRFbWFpbH1cXGAgZm9yIGNvbnRhY3QgaW5mb1xcbmAgOiAnJ31gICtcbiAgICAgICAgICAgICAgICBg4oCiIFVzZSBcXGBjbGVhcl91c2VyX2lkZW50aXR5XFxgIHRvIHJldHVybiB0byBhbm9ueW1vdXMgbW9kZWBcbiAgICAgICAgICAgICksXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgIH07XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnN0IHNhbml0aXplZCA9IFNlY3VyZUVycm9ySGFuZGxlci5zYW5pdGl6ZUVycm9yKGVycm9yKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvbnRlbnQ6IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICB0eXBlOiAndGV4dCcgYXMgY29uc3QsXG4gICAgICAgICAgICB0ZXh0OlxuICAgICAgICAgICAgICB0aGlzLndpdGhJbmRpY2F0b3IoJ+KdjCAqKlZhbGlkYXRpb24gRXJyb3IqKlxcblxcbicpICtcbiAgICAgICAgICAgICAgYCR7c2FuaXRpemVkLm1lc3NhZ2V9XFxuXFxuYCArXG4gICAgICAgICAgICAgIGBQbGVhc2UgcHJvdmlkZSBhIHZhbGlkIHVzZXJuYW1lIChhbHBoYW51bWVyaWMgY2hhcmFjdGVycywgaHlwaGVucywgdW5kZXJzY29yZXMsIGRvdHMgb25seSkuYCxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IGN1cnJlbnQgdXNlciBpZGVudGl0eSBzdGF0dXNcbiAgICogU2hvd3MgdXNlcm5hbWUsIGVtYWlsIChpZiBzZXQpLCBhbmQgYXR0cmlidXRpb24gc3RhdHVzXG4gICAqL1xuICBhc3luYyBnZXRVc2VySWRlbnRpdHkoKSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVJbml0aWFsaXplZCgpO1xuXG4gICAgY29uc3QgeyB1c2VybmFtZTogY3VycmVudFVzZXJuYW1lLCBlbWFpbCB9ID0gdGhpcy5wZXJzb25hTWFuYWdlci5nZXRVc2VySWRlbnRpdHkoKTtcblxuICAgIGlmICghY3VycmVudFVzZXJuYW1lKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBjb250ZW50OiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgdHlwZTogJ3RleHQnIGFzIGNvbnN0LFxuICAgICAgICAgICAgdGV4dDogdGhpcy53aXRoSW5kaWNhdG9yKFxuICAgICAgICAgICAgICBg8J+RpCAqKlVzZXIgSWRlbnRpdHk6IEFub255bW91cyoqXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgYPCflJIgKipTdGF0dXM6KiogQW5vbnltb3VzIG1vZGVcXG5gICtcbiAgICAgICAgICAgICAgICBg8J+TnSAqKkF0dHJpYnV0aW9uOioqIFBlcnNvbmFzIHdpbGwgdXNlIGFub255bW91cyBJRHNcXG5cXG5gICtcbiAgICAgICAgICAgICAgICBgKipUbyBzZXQgeW91ciBpZGVudGl0eToqKlxcbmAgK1xuICAgICAgICAgICAgICAgIGDigKIgVXNlOiBcXGBzZXRfdXNlcl9pZGVudGl0eSBcInlvdXItdXNlcm5hbWVcIlxcYFxcbmAgK1xuICAgICAgICAgICAgICAgIGDigKIgT3Igc2V0IGVudmlyb25tZW50IHZhcmlhYmxlOiBcXGBET0xMSE9VU0VfVVNFUj15b3VyLXVzZXJuYW1lXFxgYFxuICAgICAgICAgICAgKSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgY29udGVudDogW1xuICAgICAgICB7XG4gICAgICAgICAgdHlwZTogJ3RleHQnIGFzIGNvbnN0LFxuICAgICAgICAgIHRleHQ6IHRoaXMud2l0aEluZGljYXRvcihcbiAgICAgICAgICAgIGDwn5GkICoqVXNlciBJZGVudGl0eTogJHtjdXJyZW50VXNlcm5hbWV9KipcXG5cXG5gICtcbiAgICAgICAgICAgICAgYOKchSAqKlN0YXR1czoqKiBBdXRoZW50aWNhdGVkXFxuYCArXG4gICAgICAgICAgICAgIGDwn5GkICoqVXNlcm5hbWU6KiogJHtjdXJyZW50VXNlcm5hbWV9XFxuYCArXG4gICAgICAgICAgICAgIGAke2VtYWlsID8gYPCfk6cgKipFbWFpbDoqKiAke2VtYWlsfVxcbmAgOiAnJ31gICtcbiAgICAgICAgICAgICAgYPCfk50gKipBdHRyaWJ1dGlvbjoqKiBOZXcgcGVyc29uYXMgd2lsbCBiZSBjcmVkaXRlZCB0byBcIiR7Y3VycmVudFVzZXJuYW1lfVwiXFxuXFxuYCArXG4gICAgICAgICAgICAgIGAqKkVudmlyb25tZW50IFZhcmlhYmxlczoqKlxcbmAgK1xuICAgICAgICAgICAgICBg4oCiIFxcYERPTExIT1VTRV9VU0VSPSR7Y3VycmVudFVzZXJuYW1lfVxcYFxcbmAgK1xuICAgICAgICAgICAgICBgJHtlbWFpbCA/IGDigKIgXFxgRE9MTEhPVVNFX0VNQUlMPSR7ZW1haWx9XFxgXFxuYCA6ICcnfWAgK1xuICAgICAgICAgICAgICBgXFxuKipNYW5hZ2VtZW50OioqXFxuYCArXG4gICAgICAgICAgICAgIGDigKIgVXNlIFxcYGNsZWFyX3VzZXJfaWRlbnRpdHlcXGAgdG8gcmV0dXJuIHRvIGFub255bW91cyBtb2RlXFxuYCArXG4gICAgICAgICAgICAgIGDigKIgVXNlIFxcYHNldF91c2VyX2lkZW50aXR5IFwibmV3LXVzZXJuYW1lXCJcXGAgdG8gY2hhbmdlIHVzZXJuYW1lYFxuICAgICAgICAgICksXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ2xlYXIgdXNlciBpZGVudGl0eSBhbmQgcmV0dXJuIHRvIGFub255bW91cyBtb2RlXG4gICAqL1xuICBhc3luYyBjbGVhclVzZXJJZGVudGl0eSgpIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZUluaXRpYWxpemVkKCk7XG5cbiAgICBjb25zdCB7IHVzZXJuYW1lOiBjdXJyZW50VXNlcm5hbWUgfSA9IHRoaXMucGVyc29uYU1hbmFnZXIuZ2V0VXNlcklkZW50aXR5KCk7XG4gICAgY29uc3Qgd2FzU2V0ID0gY3VycmVudFVzZXJuYW1lICE9PSBudWxsO1xuICAgIGNvbnN0IHByZXZpb3VzVXNlciA9IGN1cnJlbnRVc2VybmFtZTtcblxuICAgIHRoaXMucGVyc29uYU1hbmFnZXIuY2xlYXJVc2VySWRlbnRpdHkoKTtcblxuICAgIC8vIEZJWDogRE1DUC1TRUMtMDA2IC0gQWRkIHNlY3VyaXR5IGF1ZGl0IGxvZ2dpbmcgZm9yIGlkZW50aXR5IGNsZWFyaW5nXG4gICAgaWYgKHdhc1NldCkge1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnRUxFTUVOVF9ERUxFVEVEJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgICBzb3VyY2U6ICdJZGVudGl0eUhhbmRsZXIuY2xlYXJVc2VySWRlbnRpdHknLFxuICAgICAgICBkZXRhaWxzOiBgVXNlciBpZGVudGl0eSBjbGVhcmVkOiBwcmV2aW91cz0ke3ByZXZpb3VzVXNlcn1gLFxuICAgICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICAgIHByZXZpb3VzVXNlcm5hbWU6IHByZXZpb3VzVXNlclxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgY29udGVudDogW1xuICAgICAgICB7XG4gICAgICAgICAgdHlwZTogJ3RleHQnIGFzIGNvbnN0LFxuICAgICAgICAgIHRleHQ6IHdhc1NldFxuICAgICAgICAgICAgPyB0aGlzLndpdGhJbmRpY2F0b3IoXG4gICAgICAgICAgICAgICAgYOKchSAqKlVzZXIgSWRlbnRpdHkgQ2xlYXJlZCoqXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgICBg8J+RpCAqKlByZXZpb3VzOioqICR7cHJldmlvdXNVc2VyfVxcbmAgK1xuICAgICAgICAgICAgICAgICAgYPCflJIgKipDdXJyZW50OioqIEFub255bW91cyBtb2RlXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgICBg8J+TnSAqKkVmZmVjdDoqKiBOZXcgcGVyc29uYXMgd2lsbCB1c2UgYW5vbnltb3VzIElEc1xcblxcbmAgK1xuICAgICAgICAgICAgICAgICAgYOKaoO+4jyAqKk5vdGU6KiogVGhpcyBvbmx5IGFmZmVjdHMgdGhlIGN1cnJlbnQgc2Vzc2lvbi5cXG5gICtcbiAgICAgICAgICAgICAgICAgIGBUbyBwZXJzaXN0IHRoaXMgY2hhbmdlLCB1bnNldCB0aGUgXFxgRE9MTEhPVVNFX1VTRVJcXGAgZW52aXJvbm1lbnQgdmFyaWFibGUuYFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICA6IHRoaXMud2l0aEluZGljYXRvcihcbiAgICAgICAgICAgICAgICBg4oS577iPICoqQWxyZWFkeSBpbiBBbm9ueW1vdXMgTW9kZSoqXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgICBg8J+RpCBObyB1c2VyIGlkZW50aXR5IHdhcyBzZXQuXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgICBgVXNlIFxcYHNldF91c2VyX2lkZW50aXR5IFwidXNlcm5hbWVcIlxcYCB0byBzZXQgeW91ciBpZGVudGl0eS5gXG4gICAgICAgICAgICAgICksXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogR2V0IGN1cnJlbnQgdXNlciBmb3IgYXR0cmlidXRpb24gcHVycG9zZXNcbiAgICogUmV0dXJucyB1c2VybmFtZSBpZiBzZXQsIG90aGVyd2lzZSBQZXJzb25hTWFuYWdlcidzIGFub255bW91cyBJRFxuICAgKi9cbiAgcHVibGljIGdldEN1cnJlbnRVc2VyRm9yQXR0cmlidXRpb24oKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5wZXJzb25hTWFuYWdlci5nZXRDdXJyZW50VXNlckZvckF0dHJpYnV0aW9uKCk7XG4gIH1cbn1cbiJdfQ==