import { StorageSharedKeyCredential , newPipeline , QueueServiceClient } from "@azure/storage-queue" ; import * as assert from "assert" ; import { configLogger } from "../../src/common/Logger" ; import { StoreDestinationArray } from "../../src/common/persistence/IExtentStore" ; import QueueConfiguration from "../../src/queue/QueueConfiguration" ; import Server from "../../src/queue/QueueServer" ; import { EMULATOR_ACCOUNT_KEY , EMULATOR_ACCOUNT_NAME , rmRecursive , sleep } from "../testutils" ; import OPTIONSRequestPolicyFactory from "./RequestPolicy/OPTIONSRequestPolicyFactory" ; import OriginPolicyFactory from "./RequestPolicy/OriginPolicyFactory" ; configLogger ( false ) ; describe ( "Queue Cors requests test" , ( ) => { const host = "127.0.0.1" ; const port = 11001 ; const metadataDbPath = "__queueTestsStorage__" ; const extentDbPath = "__extentTestsStorage__" ; const persistencePath = "__queueTestsPersistence__" ; const DEFUALT_QUEUE_PERSISTENCE_ARRAY = [ { locationId : "queueTest" , locationPath : persistencePath , maxConcurrency : 10 } ] ; const config = new QueueConfiguration ( host , port , metadataDbPath , extentDbPath , DEFUALT_QUEUE_PERSISTENCE_ARRAY , false ) ; const baseURL = `http://${ host }:${ port }/devstoreaccount1` ; const serviceClient = new QueueServiceClient ( baseURL , newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ) ; let server ; before ( async ( ) => { server = new Server ( config ) ; await server . start ( ) ; } ) ; after ( async ( ) => { await server . close ( ) ; await rmRecursive ( metadataDbPath ) ; await rmRecursive ( extentDbPath ) ; await rmRecursive ( persistencePath ) ; } ) ; it ( "OPTIONS request without cors rules in server should be fail @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; serviceProperties . cors = [ ] ; await serviceClient . setProperties ( serviceProperties ) ; const origin = "Origin" ; const requestMethod = "GET" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod ) ) ; const serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; let error ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 403 ) ; assert . ok ( error . message . includes ( "CORS not enabled or no matching rule found for this request." ) ) ; } ) ; it ( "OPTIONS request should not work without matching cors rules @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "test" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; let origin = "Origin" ; let requestMethod = "GET" ; let pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod ) ) ; let serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; let error ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 403 ) ; assert . ok ( error . message . includes ( "CORS not enabled or no matching rule found for this request." ) ) ; origin = "test" ; requestMethod = "GET" ; pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod ) ) ; serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; const res = await serviceClientForOPTIONS . getProperties ( ) ; assert . ok ( res . _response . status === 200 ) ; } ) ; it ( "OPTIONS request should not work without Origin header or matching allowedOrigins @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "test" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "Origin" ; const requestMethod = "GET" ; let pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod ) ) ; let serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; let error ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 403 ) ; assert . ok ( error . message . includes ( "CORS not enabled or no matching rule found for this request." ) ) ; pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( undefined , requestMethod ) ) ; serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 403 ) ; assert . ok ( error . message . includes ( "CORS not enabled or no matching rule found for this request." ) ) ; } ) ; it ( "OPTIONS request should not work without requestMethod header or matching allowedMethods @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "test" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "test" ; const requestMethod = "PUT" ; let pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod ) ) ; let serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; let error ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 403 ) ; assert . ok ( error . message . includes ( "CORS not enabled or no matching rule found for this request." ) ) ; pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , undefined ) ) ; serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 400 ) ; assert . ok ( error . message . includes ( "A required CORS header is not present." ) ) ; } ) ; it ( "OPTIONS request should check the defined requestHeaders @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = [ { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "test" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } , { allowedHeaders : "*" , allowedMethods : "PUT" , allowedOrigins : "test" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } , { allowedHeaders : "head*" , allowedMethods : "POST" , allowedOrigins : "test" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ] ; serviceProperties . cors = newCORS ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; let origin = "test" ; let requestMethod = "GET" ; let reqestHeaders = "head" ; let pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod , reqestHeaders ) ) ; let serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; let error ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 403 ) ; assert . ok ( error . message . includes ( "CORS not enabled or no matching rule found for this request." ) ) ; origin = "test" ; requestMethod = "GET" ; reqestHeaders = "header" ; pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod , reqestHeaders ) ) ; serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; let res = await serviceClientForOPTIONS . getProperties ( ) ; assert . ok ( res . _response . status === 200 ) ; origin = "test" ; requestMethod = "PUT" ; reqestHeaders = "head" ; pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod , reqestHeaders ) ) ; serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; res = await serviceClientForOPTIONS . getProperties ( ) ; assert . ok ( res . _response . status === 200 ) ; origin = "test" ; requestMethod = "POST" ; reqestHeaders = "hea" ; pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod , reqestHeaders ) ) ; serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; error ; try { await serviceClientForOPTIONS . getProperties ( ) ; } catch ( err ) { error = err ; } assert . ok ( error . statusCode === 403 ) ; assert . ok ( error . message . includes ( "CORS not enabled or no matching rule found for this request." ) ) ; origin = "test" ; requestMethod = "POST" ; reqestHeaders = "headerheader" ; pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod , reqestHeaders ) ) ; serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; res = await serviceClientForOPTIONS . getProperties ( ) ; assert . ok ( res . _response . status === 200 ) ; } ) ; it ( "OPTIONS request should work with matching rule containing Origion * @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "*" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "anyOrigin" ; const requestMethod = "GET" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OPTIONSRequestPolicyFactory ( origin , requestMethod ) ) ; const serviceClientForOPTIONS = new QueueServiceClient ( baseURL , pipeline ) ; const res = await serviceClientForOPTIONS . getProperties ( ) ; assert . ok ( res . _response . status === 200 ) ; } ) ; it ( "Response of request to service without cors rules should not contains cors info @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; serviceProperties . cors = [ ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "anyOrigin" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OriginPolicyFactory ( origin ) ) ; const serviceClientWithOrigin = new QueueServiceClient ( baseURL , pipeline ) ; const res = await serviceClientWithOrigin . getProperties ( ) ; assert . ok ( res [ "access-control-allow-origin" ] === undefined ) ; assert . ok ( res [ "access-control-expose-headers" ] === undefined ) ; assert . ok ( res . vary === undefined ) ; } ) ; it ( "Service with mismatching cors rules should response header Vary @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "test" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "anyOrigin" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OriginPolicyFactory ( origin ) ) ; const serviceClientWithOrigin = new QueueServiceClient ( baseURL , pipeline ) ; let res = await serviceClientWithOrigin . getProperties ( ) ; assert . ok ( res . vary !== undefined ) ; res = await serviceClient . getProperties ( ) ; assert . ok ( res . vary === undefined ) ; } ) ; it ( "Request Match rule exists that allows all origins (*) @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "*" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "anyOrigin" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OriginPolicyFactory ( origin ) ) ; const serviceClientWithOrigin = new QueueServiceClient ( baseURL , pipeline ) ; let res = await serviceClientWithOrigin . getProperties ( ) ; assert . ok ( res [ "access-control-allow-origin" ] === "*" ) ; assert . ok ( res . vary === undefined ) ; assert . ok ( res [ "access-control-expose-headers" ] !== undefined ) ; res = await serviceClient . getProperties ( ) ; assert . ok ( res [ "access-control-allow-origin" ] === undefined ) ; assert . ok ( res . vary === undefined ) ; assert . ok ( res [ "access-control-expose-headers" ] === undefined ) ; } ) ; it ( "Request Match rule exists for exact origin @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "exactOrigin" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "exactOrigin" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OriginPolicyFactory ( origin ) ) ; const serviceClientWithOrigin = new QueueServiceClient ( baseURL , pipeline ) ; const res = await serviceClientWithOrigin . getProperties ( ) ; assert . ok ( res [ "access-control-allow-origin" ] === origin ) ; assert . ok ( res . vary !== undefined ) ; assert . ok ( res [ "access-control-expose-headers" ] !== undefined ) ; } ) ; it ( "Requests with error response should apply for CORS @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "exactOrigin" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ; serviceProperties . cors = [ newCORS ] ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "exactOrigin" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OriginPolicyFactory ( origin ) ) ; const serviceClientWithOrigin = new QueueServiceClient ( baseURL , pipeline ) ; const queueClientWithOrigin = serviceClientWithOrigin . getQueueClient ( "notexistcontainer" ) ; try { await queueClientWithOrigin . getProperties ( ) ; } catch ( err ) { assert . ok ( err . response . headers . _headersMap [ "access-control-allow-origin" ] . value === origin ) ; assert . ok ( err . response . headers . _headersMap . vary !== undefined ) ; assert . ok ( err . response . headers . _headersMap [ "access-control-expose-headers" ] !== undefined ) ; } } ) ; it ( "Request Match rule in sequence @loki" , async ( ) => { const serviceProperties = await serviceClient . getProperties ( ) ; const newCORS = [ { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "exactOrigin" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } , { allowedHeaders : "header" , allowedMethods : "GET" , allowedOrigins : "*" , exposedHeaders : "*" , maxAgeInSeconds : 8888 } ] ; serviceProperties . cors = newCORS ; await serviceClient . setProperties ( serviceProperties ) ; await sleep ( 100 ) ; const origin = "exactOrigin" ; const pipeline = newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } } ) ; pipeline . factories . unshift ( new OriginPolicyFactory ( origin ) ) ; const serviceClientWithOrigin = new QueueServiceClient ( baseURL , pipeline ) ; const res = await serviceClientWithOrigin . getProperties ( ) ; assert . ok ( res [ "access-control-allow-origin" ] === origin ) ; assert . ok ( res . vary !== undefined ) ; assert . ok ( res [ "access-control-expose-headers" ] !== undefined ) ; } ) ; } ) ;