Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consume start and end partition and row keys #25293

Merged
2 commits merged into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 87 additions & 19 deletions sdk/tables/data-tables/src/sas/sasQueryParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,30 @@ export class SasQueryParameters {
*/
public readonly correlationId?: string;

/**
* Optional, but startPartitionKey must accompany startRowKey. The minimum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no lower bound on the table entities that can be accessed.
*/
public readonly startPartitionKey?: string;

/**
* Optional, but startPartitionKey must accompany startRowKey. The minimum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no lower bound on the table entities that can be accessed.
*/
public readonly startRowKey?: string;

/**
* Optional, but endPartitionKey must accompany endRowKey. The maximum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no upper bound on the table entities that can be accessed.
*/
public readonly endPartitionKey?: string;

/**
* Optional, but endPartitionKey must accompany endRowKey. The maximum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no upper bound on the table entities that can be accessed.
*/
public readonly endRowKey?: string;

/**
* Optional. IP range allowed for this SAS.
*
Expand Down Expand Up @@ -166,6 +190,10 @@ export class SasQueryParameters {
this.ipRangeInner = options.ipRange;
this.identifier = options.identifier;
this.tableName = options.tableName;
this.endPartitionKey = options.endPartitionKey;
this.endRowKey = options.endRowKey;
this.startPartitionKey = options.startPartitionKey;
this.startRowKey = options.startRowKey;

if (options.userDelegationKey) {
this.signedOid = options.userDelegationKey.signedObjectId;
Expand All @@ -186,31 +214,35 @@ export class SasQueryParameters {
*/
public toString(): string {
const params: string[] = [
"sv",
"ss",
"srt",
"spr",
"st",
"se",
"sip",
"si",
"sv", // SignedVersion
"ss", // SignedServices
"srt", // SignedResourceTypes
"spr", // SignedProtocol
"st", // SignedStart
"se", // SignedExpiry
"sip", // SignedIP
"si", // SignedIdentifier
"skoid", // Signed object ID
"sktid", // Signed tenant ID
"skt", // Signed key start time
"ske", // Signed key expiry time
"sks", // Signed key service
"skv", // Signed key version
"sr",
"sp",
"sig",
"rscc",
"rscd",
"rsce",
"rscl",
"rsct",
"saoid",
"scid",
"tn", // TableName
"sr", // signedResource
"sp", // SignedPermission
"sig", // Signature
"rscc", // Cache-Control
"rscd", // Content-Disposition
"rsce", // Content-Encoding
"rscl", // Content-Language
"rsct", // Content-Type
"saoid", // signedAuthorizedObjectId
"scid", // signedCorrelationId
"tn", // TableName,
joheredi marked this conversation as resolved.
Show resolved Hide resolved
"srk", // StartRowKey
"spk", // StartPartitionKey
"epk", // EndPartitionKey
"erk", // EndRowKey
];
const queries: string[] = [];

Expand Down Expand Up @@ -293,6 +325,18 @@ export class SasQueryParameters {
case "tn":
this.tryAppendQueryParameter(queries, param, this.tableName);
break;
case "spk":
this.tryAppendQueryParameter(queries, param, this.startPartitionKey);
break;
case "srk":
this.tryAppendQueryParameter(queries, param, this.startRowKey);
break;
case "epk":
this.tryAppendQueryParameter(queries, param, this.endPartitionKey);
break;
case "erk":
this.tryAppendQueryParameter(queries, param, this.endRowKey);
break;
}
}
return queries.join("&");
Expand Down Expand Up @@ -386,4 +430,28 @@ export interface SasQueryParametersOptions {
* This is only used for User Delegation SAS.
*/
correlationId?: string;

/**
* Optional, but startPartitionKey must accompany startRowKey. The minimum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no lower bound on the table entities that can be accessed.
*/
startPartitionKey?: string;

/**
* Optional, but startPartitionKey must accompany startRowKey. The minimum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no lower bound on the table entities that can be accessed.
*/
startRowKey?: string;

/**
* Optional, but endPartitionKey must accompany endRowKey. The maximum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no upper bound on the table entities that can be accessed.
*/
endPartitionKey?: string;

/**
* Optional, but endPartitionKey must accompany endRowKey. The maximum partition and row keys that are accessible with this shared access signature.
* Key values are inclusive. If they're omitted, there's no upper bound on the table entities that can be accessed.
*/
endRowKey?: string;
}
4 changes: 4 additions & 0 deletions sdk/tables/data-tables/src/sas/tableSasSignatureValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ export function generateTableSasQueryParameters(
ipRange: tableSasSignatureValues.ipRange,
identifier: tableSasSignatureValues.identifier,
tableName,
startPartitionKey: tableSasSignatureValues.startPartitionKey,
startRowKey: tableSasSignatureValues.startRowKey,
endPartitionKey: tableSasSignatureValues.endPartitionKey,
endRowKey: tableSasSignatureValues.endRowKey,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
import { apiVersionPolicy } from "../../src/utils/apiVersionPolicy";
import { assert } from "chai";

describe("apiVersionPolicy", () => {
it("should override the default api-version", async () => {
describe("apiVersionPolicy", function () {
it("should override the default api-version", async function () {
const expectedVersion = "2020-12-06";
const fakeClient: HttpClient = {
async sendRequest(req) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AzureNamedKeyCredential, generateTableSas } from "../../../src";
import { assert } from "chai";

// This file is empty as sas generation is not supported in browsers
describe("generateSas Browser", () => {
describe("generateSas Browser", function () {
it("should throw", function () {
try {
generateTableSas("testTable", new AzureNamedKeyCredential("keyName", "keySecret"));
Expand Down
14 changes: 7 additions & 7 deletions sdk/tables/data-tables/test/internal/continuationToken.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,35 @@ import {

import { assert } from "chai";

describe("continuation token utils", () => {
it("should encode nextPartitionKey and nextRowKey", () => {
describe("continuation token utils", function () {
it("should encode nextPartitionKey and nextRowKey", function () {
const encoded = encodeContinuationToken("foo", "bar");
assert.equal(encoded, "eyJuZXh0UGFydGl0aW9uS2V5IjoiZm9vIiwibmV4dFJvd0tleSI6ImJhciJ9");
});

it("should not encode nextRowKey if it is empty string", () => {
it("should not encode nextRowKey if it is empty string", function () {
const encoded = encodeContinuationToken("foo", "");
assert.deepEqual(decodeContinuationToken(encoded!), { nextPartitionKey: "foo" });
});

it("should encode nextPartitionKey and undefined nextRowKey", () => {
it("should encode nextPartitionKey and undefined nextRowKey", function () {
const encoded = encodeContinuationToken("foo");
assert.equal(encoded, "eyJuZXh0UGFydGl0aW9uS2V5IjoiZm9vIn0=");
});

it("should return undefined if nextPartitionKey and nextRowKey are empty", () => {
it("should return undefined if nextPartitionKey and nextRowKey are empty", function () {
const encoded = encodeContinuationToken();
assert.equal(encoded, undefined);
});

it("should decode nextPartitionKey and nextRowKey", () => {
it("should decode nextPartitionKey and nextRowKey", function () {
const decoded = decodeContinuationToken(
"eyJuZXh0UGFydGl0aW9uS2V5IjoiZm9vIiwibmV4dFJvd0tleSI6ImJhciJ9"
);
assert.deepEqual(decoded, { nextPartitionKey: "foo", nextRowKey: "bar" });
});

it("should decode nextPartitionKey and undefined nextRowKey", () => {
it("should decode nextPartitionKey and undefined nextRowKey", function () {
const decoded = decodeContinuationToken("eyJuZXh0UGFydGl0aW9uS2V5IjoiZm9vIn0=");
assert.deepEqual(decoded, { nextPartitionKey: "foo" } as any);
});
Expand Down
14 changes: 7 additions & 7 deletions sdk/tables/data-tables/test/internal/errorHandling.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { HttpClient, PipelineResponse, createHttpHeaders } from "@azure/core-res
import { TableClient, TableServiceClient } from "../../src";
import { assert } from "chai";

describe("ErrorHandling", () => {
describe("TableClient", () => {
it("should not throw on delete table not found", async () => {
describe("ErrorHandling", function () {
describe("TableClient", function () {
it("should not throw on delete table not found", async function () {
const client = new TableClient("https://example.org", "fakeTable", {
httpClient: buildTestHttpClient({ status: 404 }),
});
Expand All @@ -23,7 +23,7 @@ describe("ErrorHandling", () => {
}
});

it("should throw on delete table with non 404 error", async () => {
it("should throw on delete table with non 404 error", async function () {
const client = new TableClient("https://example.org", "fakeTable", {
httpClient: buildTestHttpClient({ status: 400 }),
});
Expand All @@ -39,8 +39,8 @@ describe("ErrorHandling", () => {
});
});

describe("TableServiceClient", () => {
it("should not throw on delete table not found", async () => {
describe("TableServiceClient", function () {
it("should not throw on delete table not found", async function () {
const client = new TableServiceClient("https://example.org", {
httpClient: buildTestHttpClient({ status: 404 }),
});
Expand All @@ -55,7 +55,7 @@ describe("ErrorHandling", () => {
}
});

it("should throw on delete table with non 404 error", async () => {
it("should throw on delete table with non 404 error", async function () {
const client = new TableServiceClient("https://example.org", {
httpClient: buildTestHttpClient({ status: 400 }),
});
Expand Down
4 changes: 4 additions & 0 deletions sdk/tables/data-tables/test/internal/fakeTestSecrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ export const expectedSas6 =
"sv=2019-02-02&ss=t&srt=sco&se=2021-12-12T01%3A00%3A00Z&sp=da&sig=TmE8AOQFacynVIVR5ljBYqY3Y3K6olfdDMLl09iRvvs%3D";
export const expectedSas7 =
"sv=2019-02-02&ss=t&srt=sco&se=2022-12-12T00%3A00%3A00Z&sp=rl&sig=y42pmN9E%2FgA2O3nGn25lx%2B%2BqQmhvh0WqFi4%2BkOPitwA%3D";
export const expectedSas8 =
"sv=2019-02-02&se=2021-12-12T01%3A00%3A00Z&sp=r&sig=FmE9Q8KiLIJUxmY2k8NHCSwApy2Y1VY17Mls1dIIJcI%3D&tn=testTable&srk=1&spk=P1";
export const expectedSas9 =
"sv=2019-02-02&se=2021-12-12T01%3A00%3A00Z&sp=r&sig=h0xXlQhXumE5Litei9gWdY3ECCORubPLUFqcbWa6Tus%3D&tn=testTable&epk=P1&erk=1";
50 changes: 37 additions & 13 deletions sdk/tables/data-tables/test/internal/node/generateSas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,25 @@ import {
expectedSas5,
expectedSas6,
expectedSas7,
expectedSas8,
expectedSas9,
} from "../fakeTestSecrets";
import { assert } from "chai";

describe("SAS generation", function () {
describe("generateTableSAS", () => {
describe("generateTableSAS", function () {
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
beforeEach(function () {
clock = sinon.useFakeTimers(new Date("2021-12-12"));
});

afterEach(() => {
afterEach(function () {
if (clock) {
clock.restore();
}
});

it("should generate a SAS token with default values", async () => {
it("should generate a SAS token with default values", async function () {
// Create the table SAS token
const tableSas = generateTableSas(
"testTable",
Expand All @@ -37,7 +39,29 @@ describe("SAS generation", function () {
assert.equal(tableSas, expectedSas1);
});

it("should generate a SAS token with explicit permissions", async () => {
it("should generate a SAS token with start partition and row keys", async function () {
// Create the table SAS token
const tableSas = generateTableSas(
"testTable",
new AzureNamedKeyCredential("keyName", "keySecret"),
{ startPartitionKey: "P1", startRowKey: "1" }
);

assert.equal(tableSas, expectedSas8);
});

it("should generate a SAS token with end partition and row keys", async function () {
// Create the table SAS token
const tableSas = generateTableSas(
"testTable",
new AzureNamedKeyCredential("keyName", "keySecret"),
{ endPartitionKey: "P1", endRowKey: "1" }
);

assert.equal(tableSas, expectedSas9);
});

it("should generate a SAS token with explicit permissions", async function () {
// Create the table SAS token
const tableSas = generateTableSas(
"testTable",
Expand All @@ -53,7 +77,7 @@ describe("SAS generation", function () {
assert.equal(tableSas, expectedSas2);
});

it("should generate a SAS token with explicit expiry", async () => {
it("should generate a SAS token with explicit expiry", async function () {
// Create the table SAS token
const tableSas = generateTableSas(
"testTable",
Expand All @@ -66,7 +90,7 @@ describe("SAS generation", function () {
assert.equal(tableSas, expectedSas3);
});

it("should generate a SAS token with identifier", async () => {
it("should generate a SAS token with identifier", async function () {
// Create the table SAS token
const tableSas = generateTableSas(
"testTable",
Expand All @@ -81,26 +105,26 @@ describe("SAS generation", function () {
});
});

describe("generateAccountSAS", () => {
describe("generateAccountSAS", function () {
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
beforeEach(function () {
clock = sinon.useFakeTimers(new Date("2021-12-12"));
});

afterEach(() => {
afterEach(function () {
if (clock) {
clock.restore();
}
});

it("should generate account SAS token with default values", async () => {
it("should generate account SAS token with default values", async function () {
// Create the table SAS token
const tableSas = generateAccountSas(new AzureNamedKeyCredential("keyName", "keySecret"));

assert.equal(tableSas, expectedSas5);
});

it("should generate a SAS token with explicit permissions", async () => {
it("should generate a SAS token with explicit permissions", async function () {
// Create the table SAS token
const tableSas = generateAccountSas(new AzureNamedKeyCredential("keyName", "keySecret"), {
permissions: {
Expand All @@ -112,7 +136,7 @@ describe("SAS generation", function () {
assert.equal(tableSas, expectedSas6);
});

it("should generate a SAS token with explicit expiry", async () => {
it("should generate a SAS token with explicit expiry", async function () {
// Create the table SAS token
const tableSas = generateAccountSas(new AzureNamedKeyCredential("keyName", "keySecret"), {
expiresOn: new Date("2022-12-12"),
Expand Down
Loading