public async validate()

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);
    }