Skip to content

Commit e9f6d40

Browse files
committed
Add integration tests for impersonation
1 parent a99f8b2 commit e9f6d40

File tree

1 file changed

+200
-39
lines changed

1 file changed

+200
-39
lines changed

test/integration/data-connect.spec.ts

Lines changed: 200 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import * as chai from 'chai';
1818
import * as chaiAsPromised from 'chai-as-promised';
19-
import { getDataConnect, ConnectorConfig } from '../../lib/data-connect/index';
19+
import { getDataConnect, ConnectorConfig, GraphqlOptions } from '../../lib/data-connect/index';
2020

2121
chai.should();
2222
chai.use(chaiAsPromised);
@@ -26,61 +26,65 @@ const expect = chai.expect;
2626
/*
2727
// Schema
2828
type User @table(key: "uid") {
29-
uid: String!
30-
name: String!
31-
address: String!
29+
uid: String!
30+
name: String!
31+
address: String!
3232
}
33+
*/
34+
type User = {
35+
uid?: string;
36+
name?: string;
37+
address?: string;
38+
// Generated
39+
emails_on_from?: Email[];
40+
};
3341

42+
/*
43+
// Schema
3444
type Email @table {
35-
subject: String!
36-
date: Date!
37-
text: String!
38-
from: User!
45+
subject: String!
46+
date: Date!
47+
text: String!
48+
from: User!
3949
}
4050
*/
51+
type Email = {
52+
subject?: string;
53+
date?: string;
54+
text?: string;
55+
from?: User;
56+
// Generated
57+
id?: string;
58+
};
4159

4260
interface UserResponse {
43-
user: {
44-
name: string;
45-
uid: string;
46-
}
61+
user: User;
4762
}
4863

4964
interface UsersResponse {
50-
users: [
51-
user: {
52-
id: string;
53-
name: string;
54-
address: string;
55-
}
56-
];
65+
users: User[];
66+
}
67+
68+
interface UserUpsertResponse {
69+
user_upsert: { uid: string; };
5770
}
5871

5972
interface UserUpdateResponse {
60-
user_upsert: { uid: string }
73+
user_update: { uid: string; };
6174
}
6275

6376
interface EmailsResponse {
64-
emails: [
65-
email: {
66-
text: string;
67-
subject: string;
68-
id: string;
69-
date: string;
70-
from: {
71-
name: string;
72-
}
73-
}
74-
];
77+
emails: Email[];
7578
}
7679

7780
interface UserVariables {
78-
id: { uid: string };
81+
id: { uid: string; };
7982
}
8083

8184
const connectorConfig: ConnectorConfig = {
8285
location: 'us-west2',
8386
serviceId: 'my-service',
87+
// serviceId: 'je-test-fdc',
8488
};
8589

8690
const userId = 'QVBJcy5ndXJ3';
@@ -90,17 +94,30 @@ describe('getDataConnect()', () => {
9094
const queryListUsers = 'query ListUsers @auth(level: PUBLIC) { users { uid, name, address } }';
9195
const queryListEmails = 'query ListEmails @auth(level: NO_ACCESS) { emails { id subject text date from { name } } }';
9296
const queryGetUserById = 'query GetUser($id: User_Key!) { user(key: $id) { uid name } }';
97+
98+
const queryListUsersImpersonation = `
99+
query ListUsers @auth(level: USER) {
100+
users(where: { uid: { eq_expr: "auth.uid" } }) { uid, name, address }
101+
}`;
102+
93103
const multipleQueries = `
94104
${queryListUsers}
95105
${queryListEmails}
96106
`;
107+
97108
const mutation = `mutation user { user_insert(data: {uid: "${userId}", address: "32 St", name: "Fred Car"}) }`;
109+
110+
const updateImpersonatedUser = `
111+
mutation UpdateUser @auth(level: USER) {
112+
user_update(key: { uid_expr: "auth.uid" }, data: { address: "32 Elm St.", name: "Fredrick" })
113+
}`;
114+
98115
const upsertUser = `mutation UpsertUser($id: String) {
99116
user_upsert(data: { uid: $id, address: "32 St.", name: "Fred" }) }`;
100117

101118
describe('executeGraphql()', () => {
102119
it('executeGraphql() successfully executes a GraphQL mutation', async () => {
103-
const resp = await getDataConnect(connectorConfig).executeGraphql<UserUpdateResponse, unknown>(
120+
const resp = await getDataConnect(connectorConfig).executeGraphql<UserUpsertResponse, unknown>(
104121
upsertUser, { variables: { id: userId } }
105122
);
106123
//{ data: { user_insert: { uid: 'QVBJcy5ndXJ3' } } }
@@ -111,18 +128,20 @@ describe('getDataConnect()', () => {
111128
it('executeGraphql() successfully executes a GraphQL', async () => {
112129
const resp = await getDataConnect(connectorConfig).executeGraphql<UsersResponse, UserVariables>(queryListUsers);
113130
expect(resp.data.users).to.be.not.empty;
114-
expect(resp.data.users[0].name).to.be.not.undefined;
115-
expect(resp.data.users[0].address).to.be.not.undefined;
131+
expect(resp.data.users.length).to.be.greaterThan(1);
132+
expect(resp.data.users[0].uid).to.not.be.undefined;
133+
expect(resp.data.users[0].name).to.not.be.undefined;
134+
expect(resp.data.users[0].address).to.not.be.undefined;
116135
});
117136

118137
it('executeGraphql() use the operationName when multiple queries are provided', async () => {
119138
const resp = await getDataConnect(connectorConfig).executeGraphql<EmailsResponse, unknown>(
120-
multipleQueries,
139+
multipleQueries,
121140
{ operationName: 'ListEmails' }
122141
);
123142
expect(resp.data.emails).to.be.not.empty;
124143
expect(resp.data.emails[0].id).to.be.not.undefined;
125-
expect(resp.data.emails[0].from.name).to.be.not.undefined;
144+
expect(resp.data.emails[0].from?.name).to.be.not.undefined;
126145
});
127146

128147
it('executeGraphql() should throw for a query error', async () => {
@@ -135,8 +154,9 @@ describe('getDataConnect()', () => {
135154
queryGetUserById,
136155
{ variables: { id: { uid: userId } } }
137156
);
138-
expect(resp.data.user.name).to.be.not.undefined;
139157
expect(resp.data.user.uid).equals(userId);
158+
expect(resp.data.user.name).to.not.be.undefined;
159+
expect(resp.data.user.address).to.be.undefined;
140160
});
141161
});
142162

@@ -145,13 +165,154 @@ describe('getDataConnect()', () => {
145165
const resp =
146166
await getDataConnect(connectorConfig).executeGraphqlRead<UsersResponse, UserVariables>(queryListUsers);
147167
expect(resp.data.users).to.be.not.empty;
168+
expect(resp.data.users.length).to.be.greaterThan(1);
169+
expect(resp.data.users[0].uid).to.not.be.undefined;
148170
expect(resp.data.users[0].name).to.be.not.undefined;
149171
expect(resp.data.users[0].address).to.be.not.undefined;
150172
});
151173

152174
it('executeGraphqlRead() should throw for a GraphQL mutation', async () => {
153175
return getDataConnect(connectorConfig).executeGraphqlRead(mutation)
154-
.should.eventually.be.rejected;
176+
.should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied');
177+
});
178+
});
179+
180+
describe('Impersonation', () => {
181+
const optsAuthorizedClaims: GraphqlOptions<undefined> = {
182+
impersonate: {
183+
authClaims: {
184+
sub: userId,
185+
email_verified: true
186+
}
187+
}
188+
};
189+
190+
const optsNonExistingClaims: GraphqlOptions<undefined> = {
191+
impersonate: {
192+
authClaims: {
193+
sub: 'non-exisiting-id',
194+
email_verified: true
195+
}
196+
}
197+
};
198+
199+
const optsUnauthorizedClaims: GraphqlOptions<undefined> = {
200+
impersonate: {
201+
unauthenticated: true
202+
}
203+
};
204+
205+
describe('USER Auth Policy', () => {
206+
it('executeGraphqlRead() successfully executes an impersonated query with authenticated claims', async () => {
207+
const resp =
208+
await getDataConnect(connectorConfig).executeGraphqlRead<UsersResponse, undefined>(
209+
queryListUsersImpersonation, optsAuthorizedClaims);
210+
expect(resp.data.users).to.be.not.empty;
211+
expect(resp.data.users.length).equals(1);
212+
expect(resp.data.users[0].uid).equals(userId);
213+
expect(resp.data.users[0].name).to.not.be.undefined;
214+
expect(resp.data.users[0].address).to.not.be.undefined;
215+
});
216+
217+
it('executeGraphqlRead() should throw for impersonated query with unauthenticated claims', async () => {
218+
return getDataConnect(connectorConfig).executeGraphqlRead(queryListUsersImpersonation, optsUnauthorizedClaims)
219+
.should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated');
220+
});
221+
222+
it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => {
223+
const resp =
224+
await getDataConnect(connectorConfig).executeGraphqlRead<UsersResponse, undefined>(
225+
queryListUsersImpersonation, optsAuthorizedClaims);
226+
expect(resp.data.users).to.be.not.empty;
227+
expect(resp.data.users.length).equals(1);
228+
expect(resp.data.users[0].uid).equals(userId);
229+
expect(resp.data.users[0].name).to.be.not.undefined;
230+
expect(resp.data.users[0].address).to.be.not.undefined;
231+
});
232+
233+
it('executeGraphql() should throw for impersonated query with unauthenticated claims', async () => {
234+
return getDataConnect(connectorConfig).executeGraphql(queryListUsersImpersonation, optsUnauthorizedClaims)
235+
.should.eventually.be.rejected.and.has.property('code', 'data-connect/unauthenticated');
236+
});
237+
238+
it('executeGraphql() successfully executes an impersonated query with non-existing authenticated claims',
239+
async () => {
240+
const resp = await getDataConnect(connectorConfig).executeGraphql<UsersResponse, undefined>(
241+
queryListUsersImpersonation, optsNonExistingClaims);
242+
// Should find no data
243+
expect(resp.data.users).to.be.empty;
244+
});
245+
246+
it('executeGraphql() successfully executes an impersonated mutation with authenticated claims',
247+
async () => {
248+
const resp = await getDataConnect(connectorConfig).executeGraphql<UserUpdateResponse, undefined>(
249+
updateImpersonatedUser, optsAuthorizedClaims);
250+
expect(resp.data.user_update.uid).equals(userId);
251+
});
252+
253+
it('executeGraphql() should throw for impersonated mutation with unauthenticated claims', async () => {
254+
return getDataConnect(connectorConfig).executeGraphql(updateImpersonatedUser, optsUnauthorizedClaims)
255+
.should.eventually.be.rejected.and.has.property('code', 'data-connect/unauthenticated');
256+
});
257+
258+
it('executeGraphql() successfully executes an impersonated mutation with non-existing authenticated claims',
259+
async () => {
260+
const resp = await getDataConnect(connectorConfig).executeGraphql<UserUpdateResponse, undefined>(
261+
updateImpersonatedUser, optsNonExistingClaims);
262+
// Should find no data
263+
expect(resp.data.user_update).to.be.null;
264+
});
265+
});
266+
267+
describe('PUBLIC Auth Policy', () => {
268+
it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => {
269+
const resp = await getDataConnect(connectorConfig).executeGraphql<UsersResponse, undefined>(
270+
queryListUsers, optsAuthorizedClaims);
271+
expect(resp.data.users).to.be.not.empty;
272+
expect(resp.data.users.length).to.be.greaterThan(1);
273+
expect(resp.data.users[0].uid).to.not.be.undefined;
274+
expect(resp.data.users[0].name).to.not.be.undefined;
275+
expect(resp.data.users[0].address).to.not.be.undefined;
276+
});
277+
278+
it('executeGraphql() successfully executes an impersonated query with unauthenticated claims', async () => {
279+
const resp = await getDataConnect(connectorConfig).executeGraphql<UsersResponse, undefined>(
280+
queryListUsers, optsUnauthorizedClaims);
281+
expect(resp.data.users).to.be.not.empty;
282+
expect(resp.data.users.length).to.be.greaterThan(1);
283+
expect(resp.data.users[0].uid).to.not.be.undefined;
284+
expect(resp.data.users[0].name).to.not.be.undefined;
285+
expect(resp.data.users[0].address).to.not.be.undefined;
286+
});
287+
288+
it('executeGraphql() successfully executes an impersonated query with non-existing authenticated claims',
289+
async () => {
290+
const resp = await getDataConnect(connectorConfig).executeGraphql<UsersResponse, undefined>(
291+
queryListUsers, optsNonExistingClaims);
292+
expect(resp.data.users).to.be.not.empty;
293+
expect(resp.data.users.length).to.be.greaterThan(1);
294+
expect(resp.data.users[0].uid).to.not.be.undefined;
295+
expect(resp.data.users[0].name).to.not.be.undefined;
296+
expect(resp.data.users[0].address).to.not.be.undefined;
297+
});
298+
});
299+
300+
describe('NO_ACCESS Auth Policy', () => {
301+
it('executeGraphql() should throw for an impersonated query with authenticated claims', async () => {
302+
return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsAuthorizedClaims)
303+
.should.eventually.be.rejected.and.has.property('code', 'data-connect/permission-denied');
304+
});
305+
306+
it('executeGraphql() should throw for an impersonated query with unauthenticated claims', async () => {
307+
return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsUnauthorizedClaims)
308+
.should.eventually.be.rejected.and.has.property('code', 'data-connect/permission-denied');
309+
});
310+
311+
it('executeGraphql() should throw for an impersonated query with non-existing authenticated claims',
312+
async () => {
313+
return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsNonExistingClaims)
314+
.should.eventually.be.rejected.and.has.property('code', 'data-connect/permission-denied');
315+
});
155316
});
156317
});
157318
});

0 commit comments

Comments
 (0)