Skip to content
This repository has been archived by the owner on Mar 22, 2018. It is now read-only.

Commit

Permalink
implementation #60
Browse files Browse the repository at this point in the history
  • Loading branch information
arafato committed Nov 30, 2017
1 parent 3a71161 commit 4089510
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 24 deletions.
19 changes: 3 additions & 16 deletions lib/core/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const CopyStatus = {

// See allowed operations below in comments
const ServiceSAS = {
Container: {
Blob: {
// Read the content, properties, metadata or block list of any blob in the container. Use any blob in the container as the source of a copy operation.
READ: 'r',
// Add a block to any append blob in the container.
Expand All @@ -120,21 +120,7 @@ const ServiceSAS = {
DELETE: 'd',
// List blobs in the container.
LIST: 'l'
},
Blob: {
// Read the content, properties, metadata and block list. Use the blob as the source of a copy operation.
READ: 'r',
// Add a block to an append blob.
ADD: 'a',
// Write a new blob, snapshot a blob, or copy a blob to a new blob.
CREATE: 'c',
// Create or write content, properties, metadata, or block list. Snapshot or lease the blob. Resize the blob (page blob only).
// Use the blob as the destination of a copy operation.
WRITE: 'w',
// Delete the blob.
DELETE: 'd'
}

}

module.exports = {
Expand All @@ -145,5 +131,6 @@ module.exports = {
Usage: Usage,
Operations: Operations,
CopyStatus: CopyStatus,
BlockListType: BlockListType
BlockListType: BlockListType,
ServiceSAS: ServiceSAS
}
4 changes: 4 additions & 0 deletions lib/core/ErrorCodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class ErrorCode {
module.exports = {
// GENERAL
InvalidXml: new ErrorCode('Invalid XML.', 400, 'One of the XML nodes specified in the request body is not supported.'),
AuthenticationFailed: new ErrorCode('AuthenticationFailed', 403, 'Server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature.'),
AuthorizationPermissionMismatch: new ErrorCode('AuthorizationPermissionMismatch', 403, 'This request is not authorized to perform this operation using this permission.'),
AuthorizationResourceTypeMismatch: new ErrorCode('AuthorizationResourceTypeMismatch', 403, 'This request is not authorized to perform this operation using this resource type.'),

// BLOB
ContainerNotFound: new ErrorCode('ContainerNotFound', 404, 'The specified container does not exist.'),
ContainerAlreadyExists: new ErrorCode('ContainerAlreadyExists', 409, 'The specified container already exists.'),
Expand Down
21 changes: 21 additions & 0 deletions lib/middleware/blob/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const BbPromise = require('bluebird'),
Operations = require('./../../core/Constants').Operations,
Usage = require('./../../core/Constants').Usage,
StorageEntityType = require('./../../core/Constants').StorageEntityType,
SasOperation = require('./../../core/Constants').ServiceSAS,
AzuriteContainerRequest = require('./../../model/blob/AzuriteContainerRequest'),
AzuriteBlobRequest = require('./../../model/blob/AzuriteBlobRequest'),
sm = require('./../../core/blob/StorageManager'),
Expand Down Expand Up @@ -36,6 +37,7 @@ const BbPromise = require('bluebird'),
BlobLeaseUsageValidation = require('./../../validation/blob/BlobLeaseUsage'),
BlockListValidation = require('./../../validation/blob/BlockList'),
AbortCopyValidation = require('./../../validation/blob/AbortCopy'),
ServiceSignatureValidation = require('./../../validation/blob/ServiceSignature'),
CopyStatusValidation = require('./../../validation/blob/CopyStatus');

module.exports = (req, res, next) => {
Expand Down Expand Up @@ -83,6 +85,7 @@ validations[Operations.Container.DELETE_CONTAINER] = (request, valContext) => {

validations[Operations.Blob.PUT_BLOB] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.CREATE })
.run(MD5Val)
.run(ContainerExistsVal)
.run(CompatibleBlobTypeVal)
Expand All @@ -95,6 +98,7 @@ validations[Operations.Blob.PUT_BLOB] = (request, valContext) => {

validations[Operations.Blob.APPEND_BLOCK] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.ADD })
.run(BlobExistsVal)
.run(ContentLengthExistsVal)
.run(BlockPageSizeVal)
Expand All @@ -108,6 +112,7 @@ validations[Operations.Blob.APPEND_BLOCK] = (request, valContext) => {

validations[Operations.Blob.DELETE_BLOB] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.DELETE })
.run(BlobExistsVal)
.run(AssociatedSnapshotDeletion, { collection: sm.db.getCollection(request.containerName) })
.run(BlobLeaseUsageValidation, { usage: Usage.Write })
Expand All @@ -116,6 +121,7 @@ validations[Operations.Blob.DELETE_BLOB] = (request, valContext) => {

validations[Operations.Blob.GET_BLOB] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.READ })
.run(BlobExistsVal)
.run(BlobCommittedVal)
.run(RangeVal)
Expand All @@ -125,11 +131,13 @@ validations[Operations.Blob.GET_BLOB] = (request, valContext) => {

validations[Operations.Container.LIST_BLOBS] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.LIST })
.run(ContainerExistsVal);
}

validations[Operations.Blob.PUT_BLOCK] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal)
.run(ContentLengthExistsVal)
.run(BlockPageSizeVal)
Expand All @@ -140,6 +148,7 @@ validations[Operations.Blob.PUT_BLOCK] = (request, valContext) => {

validations[Operations.Blob.PUT_BLOCK_LIST] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal)
.run(CompatibleBlobTypeVal)
.run(BlockListValidation, { storageManager: sm })
Expand All @@ -149,6 +158,7 @@ validations[Operations.Blob.PUT_BLOCK_LIST] = (request, valContext) => {

validations[Operations.Blob.GET_BLOCK_LIST] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.READ })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(IsOfBlobTypeVal, { entityType: StorageEntityType.BlockBlob })
Expand All @@ -157,6 +167,7 @@ validations[Operations.Blob.GET_BLOCK_LIST] = (request, valContext) => {

validations[Operations.Blob.SET_BLOB_METADATA] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(ConditionalRequestHeadersVal, { usage: Usage.Write })
Expand All @@ -165,6 +176,7 @@ validations[Operations.Blob.SET_BLOB_METADATA] = (request, valContext) => {

validations[Operations.Blob.GET_BLOB_METADATA] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.READ })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(BlobCommittedVal)
Expand All @@ -174,6 +186,7 @@ validations[Operations.Blob.GET_BLOB_METADATA] = (request, valContext) => {

validations[Operations.Blob.GET_BLOB_PROPERTIES] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.READ })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(BlobCommittedVal)
Expand All @@ -183,6 +196,7 @@ validations[Operations.Blob.GET_BLOB_PROPERTIES] = (request, valContext) => {

validations[Operations.Blob.SET_BLOB_PROPERTIES] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(ConditionalRequestHeadersVal, { usage: Usage.Write })
Expand Down Expand Up @@ -210,6 +224,7 @@ validations[Operations.Container.GET_CONTAINER_PROPERTIES] = (request, valContex

validations[Operations.Blob.PUT_PAGE] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(ContentLengthExistsVal)
Expand All @@ -225,6 +240,7 @@ validations[Operations.Blob.PUT_PAGE] = (request, valContext) => {

validations[Operations.Blob.GET_PAGE_RANGES] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.READ })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(PageAlignmentVal)
Expand All @@ -234,6 +250,7 @@ validations[Operations.Blob.GET_PAGE_RANGES] = (request, valContext) => {

validations[Operations.Container.SET_CONTAINER_ACL] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal)
.run(NumOfSignedIdentifiersVal)
.run(ConditionalRequestHeadersVal, { usage: Usage.Write })
Expand All @@ -242,12 +259,14 @@ validations[Operations.Container.SET_CONTAINER_ACL] = (request, valContext) => {

validations[Operations.Container.GET_CONTAINER_ACL] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.READ })
.run(ContainerExistsVal)
.run(ContainerLeaseUsageValidation, { usage: Usage.Other });
}

validations[Operations.Blob.SNAPSHOT_BLOB] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.CREATE })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(ConditionalRequestHeadersVal, { usage: Usage.Write })
Expand All @@ -265,6 +284,7 @@ validations[Operations.Container.LEASE_CONTAINER] = (request, valContext) => {

validations[Operations.Blob.LEASE_BLOB] = (request, valContext) => {
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal)
.run(BlobExistsVal)
.run(LeaseDurationValidation)
Expand All @@ -279,6 +299,7 @@ validations[Operations.Blob.COPY_BLOB] = (request, valContext) => {
const ret = sm._getCollectionAndContainer((request.copySourceName()).sourceContainerName),
sourceContainerProxy = ret.containerProxy;
valContext
.run(ServiceSignatureValidation, { sasOperation: SasOperation.Blob.WRITE })
.run(ContainerExistsVal, { containerProxy: sourceContainerProxy })
.run(BlobExistsVal, { blobProxy: sourceBlobProxy });

Expand Down
53 changes: 47 additions & 6 deletions lib/validation/blob/ServiceSignature.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,58 @@ class ServiceSignature {
constructor() {
}

validate({ request = undefined, moduleOptions = undefined }) {
const sm = moduleOptions.storageManager,
accessPolicy = moduleOptions.accessPolicy;

validate({ request = undefined, containerProxy = undefined, blobProxy = undefined, moduleOptions = undefined }) {
if (request.auth === undefined) {
// NOOP: No Service Signature signature was defined in the request
return;
}


if (!request.auth.sasValid) {
throw new AError(ErrorCodes.AuthenticationFailed);
}

const operation = moduleOptions.sasOperation,
accessPolicy = request.auth.accessPolicy,
resource = accessPolicy.ResourceTypes;

let start = undefined,
expiry = undefined,
permissions = undefined;

if (request.auth.accessPolicy.Id !== undefined) {
const si = (containerProxy.original.signedIdentifiers !== undefined)
? containerProxy.original.signedIdentifiers.filter((i) => {
return i.Id === request.auth.accessPolicy.Id;
})[0]
: undefined;
if (si === undefined) {
throw new AError(ErrorCodes.AuthenticationFailed);
}
start = Date.parse(si.AccessPolicy.Start);
expiry = Date.parse(si.AccessPolicy.Expiry);
permissions = si.AccessPolicy.Permission;
} else {
start = Date.parse(accessPolicy.Start); // Possibly NaN
expiry = Date.parse(accessPolicy.Expiry); // Possibly NaN
permissions = accessPolicy.Permissions;
}


// Time Validation
if (isNaN(start) || isNaN(expiry) || now < start || now > expiry) {
throw new AError(ErrorCodes.AuthenticationFailed);
}

// Permission Validation
if (!permissions.includes(operation)) {
throw new AError(ErrorCodes.AuthorizationPermissionMismatch);
}

// Resource Validation
if (resource !== undefined &&
(resource === 'b' && blobProxy === undefined) ||
(resource === 'c' && blobProxy !== undefined)) {
throw new AError(ErrorCodes.AuthorizationResourceTypeMismatch);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/xml/blob/SignedIdentifierXmlModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ class SignedIdentifiers {
this.SignedIdentifier = [];
}

addSignedIdentifier(id, start, expiry, permission) {
addSignedIdentifier(id, start, expiry, permissionlist) {
this.SignedIdentifier.push({
Id: id,
AccessPolicy: {
Start: start,
Expiry: expiry,
Permission: permission
Permission: permissionlist
}
});
}
Expand Down

0 comments on commit 4089510

Please sign in to comment.