in src/queue/authentication/AccountSASAuthenticator.ts [20:238]
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
);
const account: string = context.context.account;
const queueName: string | undefined = context.context.queue;
this.logger.debug(
// tslint:disable-next-line:max-line-length
`AccountSASAuthenticator:validate() Retrieved account name from context: ${account}, queue: ${queueName}`,
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
);
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 (!sig1Pass) {
if (accountProperties.key2 === undefined) {
return false;
}
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) {
this.logger.info(
`AccountSASAuthenticator:validate() Validate signature based account key1 and key2 failed.`,
context.contextID
);
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.`
);
}
const accountSASPermission = OPERATION_ACCOUNT_SAS_PERMISSIONS.get(
operation
);
this.logger.debug(
`AccountSASAuthenticator:validate() Got permission requirements for operation ${
Operation[operation]
} - ${JSON.stringify(accountSASPermission)}`,
context.contextID
);
if (accountSASPermission === undefined) {
throw new Error(
// tslint:disable-next-line:max-line-length
`AccountSASAuthenticator:validate() OPERATION_ACCOUNT_SAS_PERMISSIONS doesn't have configuration for operation ${
Operation[operation]
}'s account SAS permission.`
);
}
if (!accountSASPermission.validateServices(values.services)) {
throw StorageErrorFactory.getAuthorizationServiceMismatch(
context.contextID!
);
}
if (!accountSASPermission.validateResourceTypes(values.resourceTypes)) {
throw StorageErrorFactory.getAuthorizationResourceTypeMismatch(
context.contextID!
);
}
if (!accountSASPermission.validatePermissions(values.permissions)) {
throw StorageErrorFactory.getAuthorizationPermissionMismatch(
context.contextID!
);
}
this.logger.info(
`AccountSASAuthenticator:validate() Account SAS validation successfully.`,
context.contextID
);
return true;
}