public async validate()

in src/blob/authentication/BlobSASAuthenticator.ts [33:226]


  public async validate(
    req: IRequest,
    context: Context
  ): Promise<boolean | undefined> {
    this.logger.info(
      `BlobSASAuthenticator:validate() Start validation against blob service Shared Access Signature pattern.`,
      context.contextId
    );

    this.logger.debug(
      "BlobSASAuthenticator:validate() Getting account properties...",
      context.contextId
    );

    const blobContext = new BlobStorageContext(context);
    const account = blobContext.account;
    if (account === undefined) {
      throw RangeError(
        `BlobSASAuthenticator:validate() account is undefined in context.`
      );
    }

    const containerName = blobContext.container;
    if (containerName === undefined) {
      this.logger.error(
        `BlobSASAuthenticator:validate() container name is undefined in context.`,
        context.contextId
      );
      return undefined;
    }

    const blobName = blobContext.blob;
    this.logger.debug(
      // tslint:disable-next-line:max-line-length
      `BlobSASAuthenticator: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(
        blobContext.contextId!
      );
    }
    this.logger.debug(
      "BlobSASAuthenticator:validate() Got account properties successfully.",
      context.contextId
    );

    // Extract blob service SAS authentication required parameters
    const signature = this.decodeIfExist(req.getQuery("sig"));
    this.logger.debug(
      `BlobSASAuthenticator:validate() Retrieved signature from URL parameter sig: ${signature}`,
      context.contextId
    );
    if (signature === undefined) {
      this.logger.debug(
        `BlobSASAuthenticator:validate() No signature found in request. Skip blob service SAS validation.`,
        context.contextId
      );
      return undefined;
    }

    const resource = this.decodeIfExist(req.getQuery("sr"));
    if (
      resource !== BlobSASResourceType.Container &&
      resource !== BlobSASResourceType.Blob &&
      resource !== BlobSASResourceType.BlobSnapshot
    ) {
      this.logger.debug(
        // tslint:disable-next-line:max-line-length
        `BlobSASAuthenticator:validate() Signed resource type ${resource} is invalid. Skip blob service SAS validation.`,
        context.contextId
      );
      return undefined;
    }
    this.logger.debug(
      `BlobSASAuthenticator:validate() Signed resource type is ${resource}.`,
      context.contextId
    );

    const values = this.getBlobSASSignatureValuesFromRequest(
      req,
      containerName,
      blobName,
      context
    );
    if (values === undefined) {
      this.logger.info(
        // tslint:disable-next-line:max-line-length
        `BlobSASAuthenticator:validate() Failed to get valid blob service SAS values from request. Skip blob service SAS validation.`,
        context.contextId
      );
      return undefined;
    }

    this.logger.debug(
      `BlobSASAuthenticator:validate() Successfully got valid blob service 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);
    }

    if (values.signedObjectId
      || values.signedTenantId
      || values.signedService
      || values.signedVersion
      || values.signedStartsOn
      || values.signedExpiresOn) {
      this.logger.info(
        `BlobSASAuthenticator:validate() Validate signature based on user delegation key.`,
        context.contextId
      );

      if (!values.signedObjectId
        || !values.signedTenantId
        || !values.signedStartsOn
        || !values.signedExpiresOn
        || !values.signedService
        || !values.signedVersion
        || values.signedService !== "b") {
        this.logger.info(
          `BlobSASAuthenticator:validate() Signature based on user delegation key validation failed"
          }.`,
          context.contextId
        );
        throw StorageErrorFactory.getAuthorizationFailure(context.contextId!);
      }

      const savedPolicy = this.decodeIfExist(req.getQuery("si"));
      if (savedPolicy) {
        this.logger.info(
          `BlobSASAuthenticator:validate() Access policy used in UDK SAS.`,
          context.contextId
        );
        throw StorageErrorFactory.getAuthorizationFailure(context.contextId!);
      }

      this.logger.info(
        `BlobSASAuthenticator:validate() Validate UDK start and expiry time.`,
        context.contextId
      );

      if (!this.validateTime(values.signedExpiresOn!, values.signedStartsOn!)) {
        this.logger.info(
          `BlobSASAuthenticator:validate() Validate UDK start and expiry failed.`,
          context.contextId
        );
        throw StorageErrorFactory.getAuthorizationFailure(context.contextId!);
      }

      const keyValue = getUserDelegationKeyValue(
        values.signedObjectId!,
        values.signedTenantId!,
        values.signedStartsOn!,
        values.signedExpiresOn!,
        values.signedVersion!
      );
      const [sig, stringToSign] = generateBlobSASSignatureWithUDK(
        values,
        resource,
        account,
        Buffer.from(keyValue, "base64")
      );

      this.logger.debug(
        `BlobSASAuthenticator:validate() String to sign is: ${JSON.stringify(
          stringToSign
        )}`,
        context.contextId!
      );
      this.logger.debug(
        `BlobSASAuthenticator:validate() Calculated signature is: ${sig}`,
        context.contextId!
      );

      const sigPass = sig === signature;
      this.logger.info(
        `BlobSASAuthenticator:validate() Signature based on UDK ${
          sigPass ? "passed" : "failed"
        }.`,
        context.contextId
      );

      if (!sigPass) {
        return sigPass;
      }
    }