in src/blob/authentication/AccountSASAuthenticator.ts [26:216]
public async validate(
req: IRequest,
context: Context
): Promise<boolean | undefined> {
this.logger.info(
`AccountSASAuthenticator:validate() Start validation against account Shared Access Signature pattern.`,
context.contextId
);
this.logger.debug(
"AccountSASAuthenticator:validate() Getting account properties...",
context.contextId
);
// Doesn't create a BlobContext here because wants to move this class into common
const account: string = context.context.account;
const containerName: string | undefined = context.context.container;
const blobName: string | undefined = context.context.blob;
this.logger.debug(
// tslint:disable-next-line:max-line-length
`AccountSASAuthenticator:validate() Retrieved account name from context: ${account}, container: ${containerName}, blob: ${blobName}`,
context.contextId
);
// TODO: Make following async
const accountProperties = this.accountDataStore.getAccount(account);
if (accountProperties === undefined) {
throw StorageErrorFactory.ResourceNotFound(
context.contextId!
);
}
this.logger.debug(
"AccountSASAuthenticator:validate() Got account properties successfully.",
context.contextId
);
const signature = this.decodeIfExist(req.getQuery("sig"));
this.logger.debug(
`AccountSASAuthenticator:validate() Retrieved signature from URL parameter sig: ${signature}`,
context.contextId
);
const values = this.getAccountSASSignatureValuesFromRequest(req);
if (values === undefined) {
this.logger.info(
`AccountSASAuthenticator:validate() Failed to get valid account SAS values from request.`,
context.contextId
);
return false;
}
this.logger.debug(
`AccountSASAuthenticator:validate() Successfully got valid account SAS values from request. ${JSON.stringify(
values
)}`,
context.contextId
);
if (!context.context.loose && values.encryptionScope !== undefined)
{
throw new StrictModelNotSupportedError("SAS Encryption Scope 'ses'", context.contextId);
}
this.logger.info(
`AccountSASAuthenticator:validate() Validate signature based account key1.`,
context.contextId
);
const [sig1, stringToSign1] = generateAccountSASSignature(
values,
account,
accountProperties.key1
);
this.logger.debug(
`AccountSASAuthenticator:validate() String to sign is: ${JSON.stringify(
stringToSign1
)}`,
context.contextId!
);
this.logger.debug(
`AccountSASAuthenticator:validate() Calculated signature is: ${sig1}`,
context.contextId!
);
const sig1Pass = sig1 === signature;
this.logger.info(
`AccountSASAuthenticator:validate() Signature based on key1 validation ${
sig1Pass ? "passed" : "failed"
}.`,
context.contextId
);
if (accountProperties.key2 !== undefined) {
this.logger.info(
`AccountSASAuthenticator:validate() Account key2 is not empty, validate signature based account key2.`,
context.contextId
);
const [sig2, stringToSign2] = generateAccountSASSignature(
values,
account,
accountProperties.key2
);
this.logger.debug(
`AccountSASAuthenticator:validate() String to sign is: ${JSON.stringify(
stringToSign2
)}`,
context.contextId!
);
this.logger.debug(
`AccountSASAuthenticator:validate() Calculated signature is: ${sig2}`,
context.contextId!
);
const sig2Pass = sig2 === signature;
this.logger.info(
`AccountSASAuthenticator:validate() Signature based on key2 validation ${
sig2Pass ? "passed" : "failed"
}.`,
context.contextId
);
if (!sig2Pass && !sig1Pass) {
this.logger.info(
`AccountSASAuthenticator:validate() Validate signature based account key1 and key2 failed.`,
context.contextId
);
return false;
}
} else {
if (!sig1Pass) {
return false;
}
}
// When signature validation passes, we enforce account SAS validation
// Any validation errors will stop this request immediately
this.logger.info(
`AccountSASAuthenticator:validate() Validate start and expiry time.`,
context.contextId
);
if (!this.validateTime(values.expiryTime, values.startTime)) {
this.logger.info(
`AccountSASAuthenticator:validate() Validate start and expiry failed.`,
context.contextId
);
throw StorageErrorFactory.getAuthorizationFailure(context.contextId!);
}
this.logger.info(
`AccountSASAuthenticator:validate() Validate IP range.`,
context.contextId
);
if (!this.validateIPRange()) {
this.logger.info(
`AccountSASAuthenticator:validate() Validate IP range failed.`,
context.contextId
);
throw StorageErrorFactory.getAuthorizationSourceIPMismatch(
context.contextId!
);
}
this.logger.info(
`AccountSASAuthenticator:validate() Validate request protocol.`,
context.contextId
);
if (!this.validateProtocol(values.protocol, req.getProtocol())) {
this.logger.info(
`AccountSASAuthenticator:validate() Validate protocol failed.`,
context.contextId
);
throw StorageErrorFactory.getAuthorizationProtocolMismatch(
context.contextId!
);
}
const operation = context.operation;
if (operation === undefined) {
throw new Error(
// tslint:disable-next-line:max-line-length
`AccountSASAuthenticator:validate() operation shouldn't be undefined. Please make sure DispatchMiddleware is hooked before authentication related middleware.`
);
}
else if (operation === Operation.Service_GetUserDelegationKey) {
this.logger.info(
`AccountSASAuthenticator:validate() Service_GetUserDelegationKey requires OAuth credentials"
}.`,
context.contextId
);
throw StorageErrorFactory.getAuthenticationFailed(context.contextId!,
AUTHENTICATION_BEARERTOKEN_REQUIRED);
}