Skip to content

Commit

Permalink
multitenant fix
Browse files Browse the repository at this point in the history
  • Loading branch information
vzakharchenko committed May 26, 2020
1 parent e56e830 commit 401c1fc
Show file tree
Hide file tree
Showing 20 changed files with 323 additions and 62 deletions.
35 changes: 29 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,30 @@ export async function authorization(event, context, callback) {
}), callback);
}
```
## 3. Create JWKS endpoint by Lambda:Edge
## 3. protect Url with custom response handler


lamdaEdge.routes.addProtected(
'/',
keycloakJson,
{
enforce: {
enabled: true,
resource: {
name: 'tenantResource',
},
},
responseHandler: (request, options)=>{
const uri = request.uri;
if (uri.startsWith('/callback') ||
uri.startsWith('callback')) {
return callBackPageHandle;
}
}
}
);

## 4. Create JWKS endpoint by Lambda:Edge

```javascript
import { lamdaEdge } from 'keycloak-lambda-authorizer';
Expand All @@ -400,7 +423,7 @@ export async function authorization(event, context, callback) {
```


## 4. Public url
## 5. Public url

```javascript
import { lamdaEdge } from 'keycloak-lambda-authorizer';
Expand All @@ -425,7 +448,7 @@ export async function authorization(event, context, callback) {
}
```

## 5. Custom Url Handler
## 6. Custom Url Handler

```javascript
import { lamdaEdge } from 'keycloak-lambda-authorizer';
Expand Down Expand Up @@ -457,7 +480,7 @@ export async function authorization(event, context, callback) {
}
```

## 6. Custom Url Handler with Lambda:Edge [EventType](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html)
## 7. Custom Url Handler with Lambda:Edge [EventType](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html)


```javascript
Expand Down Expand Up @@ -494,7 +517,7 @@ export async function authorization(event, context, callback) {
}
```

# 7. Implementation For Custom Service or non amazon cloud
# 8. Implementation For Custom Service or non amazon cloud

```javascript
import { adapter } from 'keycloak-lambda-authorizer';
Expand Down Expand Up @@ -533,7 +556,7 @@ async function handler(request,response) {
}
```

## 8. protect Url with keycloak function
## 9. protect Url with keycloak function

```javascript
import { lamdaEdge } from 'keycloak-lambda-authorizer';
Expand Down
47 changes: 42 additions & 5 deletions __tests__/src/edge/routers/CallbackTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('testing callback', () => {
lambdaEdgeUtils.validateState.mockImplementation(() => ({ s: '/' }));
clientAuthorization.getTokenByCode.mockImplementation(async () => ({ refresh_token: 'REFRESH_TOKEN', access_token: 'ACCESS_TOKEN' }));
clientAuthorization.exchangeRPT.mockImplementation(async () => ({ refresh_token: 'REFRESH_TOKEN', access_token: 'ACCESS_TOKEN' }));
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test' } }));
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test', tenants: { test: { resNew: {} } } } }));
cookiesUtils.getCookie.mockImplementation(() => ({
session: 's',
sessionToken: 'Session_TOKEN',
Expand Down Expand Up @@ -67,7 +67,7 @@ describe('testing callback', () => {
lambdaEdgeUtils.validateState.mockImplementation(() => null);
await callbackHandler({ headers: [] }, {
logger: console,
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'test', resource: 'res' }),
route: { unauthorized, internalServerError },
}, (error, response) => {
expect(response).toEqual({
Expand All @@ -82,7 +82,7 @@ describe('testing callback', () => {
clientAuthorization.getTokenByCode.mockImplementation(() => { throw new Error('Test Error'); });
await callbackHandler({ headers: [] }, {
logger: console,
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'test', resource: 'res' }),
route: { unauthorized, internalServerError },
}, (error, response) => {
expect(response).toEqual({
Expand All @@ -99,7 +99,7 @@ describe('testing callback', () => {
logger: console,
sessionManager,
enforce: { enabled: true },
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'test', resource: 'res' }),
route: { unauthorized, internalServerError },
}, (error, response) => {
expect(response).toEqual({
Expand Down Expand Up @@ -129,12 +129,49 @@ describe('testing callback', () => {
});
});

test('test Success Logout ', async () => {
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test', tenants: { test: { res: {} } } } }));

await callbackHandler({ headers: [] }, {
logger: console,
sessionManager,
enforce: { enabled: true },
keycloakJson: () => ({ realm: 'test', resource: 'res' }),
route: { unauthorized, internalServerError },
}, (error, response) => {
expect(response).toEqual({
body: 'ID token retrieved.',
headers: {
location: [
{
key: 'Location',
value: '/test/res/logout',
},
],
'set-cookie': [
{
key: 'Set-Cookie',
},
{
key: 'Set-Cookie',
},
{
key: 'Set-Cookie',
},
],
},
status: '302',
statusDescription: 'Found',
});
});
});

test('test Success ', async () => {
await callbackHandler({ headers: [] }, {
logger: console,
sessionManager,
enforce: {},
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'test', resource: 'res' }),
route: { unauthorized, internalServerError },
}, (error, response) => {
expect(response).toEqual({
Expand Down
24 changes: 23 additions & 1 deletion __tests__/src/edge/routers/LogoutTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,38 @@ jest.mock('cookie');
jest.mock('querystring');
jest.mock('../../../../src/edge/lambdaEdgeUtils');
jest.mock('../../../../src/utils/restCalls');
jest.mock('../../../../src/utils/TokenUtils');
jest.mock('../../../../src/utils/cookiesUtils');

const qs = require('querystring');
const { tenantLogout } = require('../../../../src/edge/routes/Logout');
const cookiesUtils = require('../../../../src/utils/cookiesUtils');
const { decodeAccessToken } = require('../../../../src/utils/TokenUtils');

const sessionManager = {
checkSession: async () => true,
deleteTenantSession: async () => ({}),
updateSessionToken: async () => 'SESSION_JWT',
};

describe('testing logout', () => {
beforeEach(() => {
qs.parse.mockImplementation(() => ({ url: '/url' }));
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test', exp: 1000 } }));
cookiesUtils.getCookie.mockImplementation(() => ({
session: 's',
sessionToken: 'Session_TOKEN',
}));
});

afterEach(() => {
});

test('test tenantLogout', async () => {
const ret = await tenantLogout({}, { keycloakJson: () => ({ realm: 'name', resource: 'resource' }) });
const ret = await tenantLogout({}, {
sessionManager,
keycloakJson: () => ({ realm: 'name', resource: 'resource' }),
});
expect(ret).toEqual({
body: 'Redirect to logout page',
headers: {
Expand All @@ -31,6 +50,9 @@ describe('testing logout', () => {
{
key: 'Set-Cookie',
},
{
key: 'Set-Cookie',
},
],
},
status: '302',
Expand Down
75 changes: 65 additions & 10 deletions __tests__/src/edge/routers/utils/redirectAuthServerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const sessionManager = {

describe('testing redirectAuthServer', () => {
beforeEach(() => {
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test' } }));
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test', tenants: { testRealm: { resource: {} } } } }));
getActiveToken.mockImplementation(async () => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test' } }));
cookiesUtils.getCookie.mockImplementation(() => ({
session: 's',
Expand All @@ -26,7 +26,7 @@ describe('testing redirectAuthServer', () => {
});

test('test refreshResponse', async () => {
const refreshResponse = redirectAuthServer.refreshResponse('token', 'refreshToken', { keycloakJson: () => ({}) });
const refreshResponse = redirectAuthServer.refreshResponse('token', 'refreshToken', { keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }) });
expect(refreshResponse).toEqual({
headers: {
'set-cookie': [
Expand All @@ -42,7 +42,7 @@ describe('testing redirectAuthServer', () => {
});

test('test refreshResponse without refreshToken', async () => {
const refreshResponse = redirectAuthServer.refreshResponse('token', null, { keycloakJson: () => ({}) });
const refreshResponse = redirectAuthServer.refreshResponse('token', null, { keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }) });
expect(refreshResponse).toEqual({
status: '200',
statusDescription: 'OK',
Expand All @@ -59,7 +59,7 @@ describe('testing redirectAuthServer', () => {
body: 'Redirecting to OIDC provider',
});
}, {
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
request: {},
logger: console,
});
Expand All @@ -74,7 +74,7 @@ describe('testing redirectAuthServer', () => {
body: 'Redirecting to OIDC provider',
});
}, {
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
request: {},
sessionManager: {
checkSession: async () => false,
Expand All @@ -93,7 +93,7 @@ describe('testing redirectAuthServer', () => {
body: 'Redirecting to OIDC provider',
});
}, {
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
request: {},
sessionManager,
logger: console,
Expand All @@ -111,7 +111,62 @@ describe('testing redirectAuthServer', () => {
statusDescription: 'Unauthorized',
});
}, {
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
request: {},
sessionManager,
route: { unauthorized },
logger: console,
});
expect(resp).toEqual(null);
});


test('test checkToken redirect error', async () => {
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test', tenants: { testRealm: { } } } }));
const resp = await redirectAuthServer.checkToken((error, response) => {
expect(response).toEqual({
body: 'Redirecting to OIDC provider',
status: '302',
statusDescription: 'Found',
});
}, {
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
request: {},
sessionManager,
route: { unauthorized },
logger: console,
});
expect(resp).toEqual(null);
});

test('test checkToken redirect error Realm', async () => {
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test', tenants: { } } }));
const resp = await redirectAuthServer.checkToken((error, response) => {
expect(response).toEqual({
body: 'Redirecting to OIDC provider',
status: '302',
statusDescription: 'Found',
});
}, {
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
request: {},
sessionManager,
route: { unauthorized },
logger: console,
});
expect(resp).toEqual(null);
});

test('test checkToken redirect error Tenants', async () => {
decodeAccessToken.mockImplementation(() => ({ accessToken: 'TOKEN', accessTokenDecode: { email: 'test@test' } }));
const resp = await redirectAuthServer.checkToken((error, response) => {
expect(response).toEqual({
body: 'Redirecting to OIDC provider',
status: '302',
statusDescription: 'Found',
});
}, {
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
request: {},
sessionManager,
route: { unauthorized },
Expand All @@ -124,15 +179,15 @@ describe('testing redirectAuthServer', () => {
await redirectAuthServer.checkToken(() => {
throw new Error('unexpected state');
}, {
keycloakJson: () => ({ request: {} }),
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
sessionManager,
logger: console,
});
});

test('test redirectToKeycloak', async () => {
const resp = await redirectAuthServer.redirectToKeycloak({}, {
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
sessionManager,
logger: console,
}, 'ttt');
Expand All @@ -144,7 +199,7 @@ describe('testing redirectAuthServer', () => {
});
test('test responseWithKeycloakRedirectToLoginPage', async () => {
await redirectAuthServer.responseWithKeycloakRedirectToLoginPage({}, {
keycloakJson: () => ({}),
keycloakJson: () => ({ realm: 'testRealm', resource: 'resource' }),
sessionManager,
request: {},
logger: console,
Expand Down
File renamed without changes
File renamed without changes
Binary file added docs/tenant.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 8 additions & 8 deletions example/keycloak-authorizer/serverless/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
"author": "vzakharchenko",
"license": "Apache-2.0",
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-decorators": "^7.6.0",
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
"@babel/core": "^7.9.6",
"@babel/plugin-proposal-decorators": "^7.8.3",
"@babel/plugin-proposal-object-rest-spread": "^7.9.6",
"@babel/plugin-transform-async-to-generator": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.6.3",
"@babel/plugin-transform-runtime": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@babel/preset-es2015": "^7.0.0-beta.53",
"@babel/register": "^7.6.2",
"@babel/register": "^7.9.0",
"babel-loader": "^8.1.0",
"@babel/runtime": "^7.9.2",
"@babel/runtime": "^7.9.6",
"copy-webpack-plugin": "*",
"eslint": "*",
"eslint-config-airbnb": "*",
Expand All @@ -32,7 +32,7 @@
"serverless": "*",
"serverless-offline": "*",
"serverless-webpack": "*",
"webpack": "^4.42.1",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
},
"dependencies": {
Expand Down
Loading

0 comments on commit 401c1fc

Please sign in to comment.