in features/newRepositoryLockdown.ts [294:539]
async lockdownIfNecessary(action: 'created' | 'transferred', username: string, thirdPartyId: number, transferSourceRepositoryLogin: string): Promise<boolean> {
const lockdownLog: string[] = [];
// reconfirm that the new repository system is enabled for this organization
if (!this.organization.isNewRepositoryLockdownSystemEnabled()) {
return false;
}
const companySpecific = getCompanySpecificDeployment();
const lockdownForks = this.organization.isForkLockdownSystemEnabled();
const lockdownTransfers = this.organization.isTransferLockdownSystemEnabled();
lockdownLog.push(`Confirmed that the ${this.organization.name} organization has opted in to the new repository lockdown system`);
if (lockdownForks) {
lockdownLog.push('Confirmed that the additional fork lockdown feature is enabled for this org');
}
if (lockdownTransfers) {
lockdownLog.push('Confirmed that the additional transfer lockdown feature is enabled for this org');
}
const setupUrl = `${this.organization.absoluteBaseUrl}wizard?existingreponame=${this.repository.name}&existingrepoid=${this.repository.id}`;
let isTransfer = action === 'transferred';
if (isTransfer && !lockdownTransfers) {
return false; // no need to do special transfer logic
}
if (isTransfer && transferSourceRepositoryLogin) {
const isInternalTransfer = this.operations.isManagedOrganization(transferSourceRepositoryLogin);
if (isInternalTransfer) {
// BUSINESS RULE: If the organization is configured in the system, no need to lock it down...
// CONSIDER: should there be a feature flag for this behavior, to allow managed-to-managed org transfers without lockdown?
// CONSIDER: notify operations that a transfer happened
return false;
}
}
const lowercaseUsername = username.toLowerCase();
// any repository created by a bot *is ok* and will not be locked down. If this is an issue, having an approved list of permitted bots to create repos would be one way to approach this loophole. Non-bot users cannot have brackets in their names.
if (lowercaseUsername.includes(botBracket)) {
// CONSIDER: send operations an e-mail FYI when a bot account is used?
return false;
}
lockdownLog.push(`Confirmed that the repository was not ${action} by a bot`);
// a repository created by one of the operations accounts in the allowed list is OK and will not be locked down
const systemAccounts = new Set(this.operations.systemAccountsByUsername.map(username => username.toLowerCase()));
if (systemAccounts.has(lowercaseUsername)) {
return false;
}
lockdownLog.push(`Confirmed that the repository was not ${action} by any of the system accounts: ${Array.from(systemAccounts.values()).join(', ')}`);
await this.lockdownRepository(lockdownLog, systemAccounts, username);
let link: ICorporateLink = null;
try {
link = await this.operations.getLinkByThirdPartyId(thirdPartyId.toString());
} catch (noLinkError) {
lockdownLog.push(`No corporate link available for the GitHub username ${username} that created the repository`);
}
let isForkAdministratorLocked = false;
let isForkParentManagedBySystem = false;
let upstreamLogin = this.repository.parent?.owner?.login;
if (!upstreamLogin && this.repository?.fork === true) {
const moreEntity = await this.repository.getDetails();
upstreamLogin = moreEntity?.parent?.owner?.login;
}
try {
// Repository metadata is used to lock down the security of the repository system. Only
// a complete system administrator or the initial creator of a repository is able to
// complete the initial repository setup process.
let repositoryMetadata: RepositoryMetadataEntity = null;
try {
repositoryMetadata = await this.repositoryMetadataProvider.getRepositoryMetadata(this.repository.id.toString());
} catch (doesNotExist) {
// ignore: 404 is standard here
}
let lockdownState = RepositoryLockdownState.Locked;
if (action === 'created' && this.repository.fork && lockdownForks) {
lockdownState = RepositoryLockdownState.AdministratorLocked;
isForkAdministratorLocked = true;
lockdownLog.push('The repository is a fork and will be administrator locked');
if (upstreamLogin && this.operations.isManagedOrganization(upstreamLogin)) {
lockdownLog.push(`The parent organization, ${upstreamLogin}, is also an organization managed by the company.`);
isForkParentManagedBySystem = true;
}
}
if (repositoryMetadata) {
lockdownLog.push(`Repository metadata already exists for repository ID ${this.repository.id}`);
const updateMetadata = this.populateRepositoryMetadata(repositoryMetadata, username, thirdPartyId, link, transferSourceRepositoryLogin);
await this.repositoryMetadataProvider.updateRepositoryMetadata(updateMetadata);
lockdownLog.push(`Updated the repository metadata with username and link information`);
} else {
repositoryMetadata = this.populateRepositoryMetadata(new RepositoryMetadataEntity(), username, thirdPartyId, link, transferSourceRepositoryLogin);
repositoryMetadata.created = new Date();
repositoryMetadata.lockdownState = lockdownState;
repositoryMetadata.repositoryId = this.repository.id.toString();
repositoryMetadata.repositoryName = this.repository.name;
repositoryMetadata.organizationName = this.organization.name;
repositoryMetadata.organizationId = this.organization.id.toString();
repositoryMetadata.initialRepositoryDescription = this.repository.description;
repositoryMetadata.initialRepositoryHomepage = this.repository.homepage;
repositoryMetadata.initialRepositoryVisibility = this.repository.private ? GitHubRepositoryVisibility.Private : GitHubRepositoryVisibility.Public;
await this.repositoryMetadataProvider.createRepositoryMetadata(repositoryMetadata);
lockdownLog.push(`Created the initial repository metadata indicating the repo was created by ${username}`);
}
} catch (metadataSystemError) {
console.dir(metadataSystemError);
lockdownLog.push(`While writing repository metadata an error: ${metadataSystemError.message}`);
}
let patchChanges: IRepoPatch = {};
if (!isForkAdministratorLocked && !isTransfer && !this.repository.private) {
lockdownLog.push('Preparing to hide the public repository pending setup (V2)');
patchChanges.private = true;
}
if (!isForkAdministratorLocked) {
lockdownLog.push('Updating the description and web site to point at the setup wizard (V2)');
lockdownLog.push(`Will direct the user to ${setupUrl}`);
patchChanges.description = `${setupRepositorySubstring} ${setupUrl}`;
patchChanges.homepage = setupUrl;
}
if (Object.getOwnPropertyNames(patchChanges).length > 0) {
try {
const descriptiveUpdate = Object.getOwnPropertyNames(patchChanges).map(key => {
return `${key}=${patchChanges[key]}`
}).join(', ');
lockdownLog.push(`Updating repository with patch ${descriptiveUpdate}`);
await this.repository.update(patchChanges);
} catch (hideError) {
lockdownLog.push(`Error while trying to update the new repo: ${hideError} (V2)`);
}
}
try {
await this.tryCreateReadme(this.repository, lockdownLog);
} catch (readmeError) {
lockdownLog.push(`Error with README updates: ${readmeError}`);
}
let mailSentToCreator = false;
const operationsMails = [ this.operations.getRepositoriesNotificationMailAddress() ];
const defaultAdministrativeUnlockUrl = `${this.repository.absoluteBaseUrl}administrativeLock`;
const lockdownMailContent: IMailToLockdownRepo = {
username,
log: lockdownLog,
organization: this.organization,
repository: this.repository,
linkToDeleteRepository: this.repository.absoluteBaseUrl + 'delete',
linkToClassifyRepository: setupUrl,
linkToAdministrativeUnlockRepository: companySpecific?.urls?.getAdministrativeUnlockUrl(this.repository) || defaultAdministrativeUnlockUrl,
mailAddress: null,
link,
isForkAdministratorLocked,
};
lockdownLog.push(`The repo can be unlocked at ${lockdownMailContent.linkToClassifyRepository}`);
const repoActionType = this.repository.fork ? 'forked' : (isTransfer ? 'transferred' : 'created');
const stateVerb = isTransfer ? 'transferred' : 'new';
const forkUnlockMail = this.operations.config.brand?.forkApprovalMail || this.operations.config.brand?.operationsMail;
if (link) {
try {
const mailAddress = link.corporateMailAddress || await this.operations.getMailAddressFromCorporateUsername(link.corporateUsername);
const repoName = this.repository.name;
const subject = isForkAdministratorLocked ? `Your new fork requires administrator approval: ${repoName} (${username})` : `Please complete the setup of your ${stateVerb} GitHub repository ${repoName} (${username})`;
if (mailAddress) {
lockdownMailContent.mailAddress = mailAddress;
const companyName = this.operations.config.brand.companyName;
let managerInfo: ICachedEmployeeInformation = null;
let reasonInfo = `This mail was sent to: ${mailAddress}`;
try {
const providers = this.operations.providers;
let shouldTryNotifyManager = true;
if (providers?.customizedNewRepositoryLogic) { // this is a hack around the new repo custom logic
const customContext = providers.customizedNewRepositoryLogic.createContext({} /* "request" */);
shouldTryNotifyManager = providers.customizedNewRepositoryLogic.shouldNotifyManager(customContext, link.corporateId);
}
if (shouldTryNotifyManager) {
managerInfo = await this.operations.getCachedEmployeeManagementInformation(link.corporateId);
if (managerInfo && managerInfo.managerMail) {
reasonInfo += ` and manager ${managerInfo.managerMail}`;
}
}
} catch (managerInfoError) {
console.dir(managerInfoError);
}
const mailView = companySpecific?.views?.email?.repository?.newDirect || defaultMailTemplate;
const mailToCreator: IMail = {
to: mailAddress,
subject,
content: await this.operations.emailRender(mailView, {
reason: (`You just ${repoActionType} a repository on GitHub and have additional actions required to gain access to continue to use it after classification.
${reasonInfo}.`),
headline: isForkAdministratorLocked ? 'Fork approval required' : `Setup your ${stateVerb} repository`,
notification: isForkAdministratorLocked ? 'action' : 'information',
app: `${companyName} GitHub`,
isMailToCreator: true,
lockdownMailContent,
isForkAdministratorLocked,
isForkParentManagedBySystem,
upstreamLogin,
linkToAdministrativeUnlockRepository: companySpecific?.urls?.getAdministrativeUnlockUrl(this.repository) || defaultAdministrativeUnlockUrl,
action,
forkUnlockMail,
operationsMail: operationsMails.join(','),
transferSourceRepositoryLogin,
}),
};
if (managerInfo && managerInfo.managerMail) {
mailToCreator.cc = managerInfo.managerMail;
}
await this.operations.sendMail(mailToCreator);
lockdownLog.push(`sent an e-mail to the person who ${repoActionType} the repository ${mailAddress} (corporate username: ${link.corporateUsername})`);
mailSentToCreator = true;
} else {
lockdownLog.push(`no e-mail address available for the corporate username ${link.corporateUsername}`);
}
} catch (noLinkOrEmail) {
console.dir(noLinkOrEmail);
}
}
if (operationsMails) {
try {
const subject = isForkAdministratorLocked ? `New fork ${this.organization.name}/${this.repository.name} requires approval - forked by ${username}` : `Repository ${repoActionType}: ${this.organization.name}/${this.repository.name} (by ${username})`;
const mailToOperations: IMail = {
to: operationsMails,
subject,
content: await this.operations.emailRender('newrepolockdown', {
reason: (`A user just ${repoActionType} this repository directly on GitHub. As the operations contact for this system, you are receiving this e-mail.
This mail was sent to: ${operationsMails.join(', ')}`),
headline: isForkAdministratorLocked ? `Fork ${this.organization.name}/${this.repository.name} by ${username}` : `Repo (${stateVerb}) ${this.organization.name}/${this.repository.name} ${repoActionType} by ${username}`,
notification: 'information',
app: `${this.operations.config.brand.companyName} GitHub`,
isMailToOperations: true,
lockdownMailContent,
forkUnlockMail,
transferSourceRepositoryLogin,
action,
mailSentToCreator,
isForkAdministratorLocked,
}),
};
await this.operations.sendMail(mailToOperations);
lockdownLog.push(`sent an e-mail to the operations contact(s): ${operationsMails.join(', ')}`);
} catch (mailIssue) {
console.dir(mailIssue);
}
}
const insights = this.operations.insights;
if (insights) {
insights.trackMetric({ name: 'LockedRepos', value: 1 });
let metricName = isForkAdministratorLocked ? 'LockedForks' : 'LockedDirectRepos';
if (isTransfer) {
metricName = 'LockedTransfers';
}
insights.trackMetric({ name: metricName, value: 1 });
}
console.dir(lockdownLog);
return true;
}