diff --git a/lib/util/index.js b/lib/util/index.js index 605bffc..4722483 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -10,28 +10,35 @@ const { async function fetchAccessToken(credentials, jwt) { let decoded_token_jwt = decodeAccessToken(jwt); - let access_token = cache.get(decoded_token_jwt.email); // to check if token exists + let subdomain = cds.context.user?.tokenInfo?.getPayload()?.ext_attr?.zdn; + let cache_key = decoded_token_jwt.email+"_"+subdomain; + let access_token = cache.get(cache_key); // to check if token exists if (access_token === undefined) { access_token = await generateSDMBearerToken(credentials, jwt); let user = decodeAccessToken(access_token).email; - cache.set(user, access_token, 11 * 3600); //expires after 11 hours + let subdomain = cds.context.user?.tokenInfo?.getPayload()?.ext_attr?.zdn; + let cache_key = user+"_"+subdomain; + cache.set(cache_key, access_token, 11 * 3600); //expires after 11 hours } else { let decoded_token = decodeAccessToken(access_token); if (isTokenExpired(decoded_token.exp)) { access_token = generateSDMBearerToken(credentials, jwt); - cache.del(decoded_token.email); - cache.set(decoded_token.email, access_token, 11 * 3600); //expires after 11 hours + let subdomain = cds.context.user?.tokenInfo?.getPayload()?.ext_attr?.zdn; + let cache_key = decoded_token.email+"_"+subdomain; + cache.del(cache_key); + cache.set(cache_key, access_token, 11 * 3600); //expires after 11 hours } } return access_token; } async function generateSDMBearerToken(credentials, jwt) { + let subdomain = cds.context.user?.tokenInfo?.getPayload()?.ext_attr?.zdn; return new Promise(function (resolve, reject) { requests.requestUserToken( jwt, credentials.uaa, - null, null, null, null, (error, response) => { + null, null, subdomain, null, (error, response) => { if (error) { console.error( `Response error while fetching access token ${response.statusCode}` @@ -76,11 +83,12 @@ function decodeAccessToken(jwtEncoded) { } async function getClientCredentialsToken(credentials) { - const access_token = cache.get("SDM_ACCESS_TOKEN"); // to check if token exists + let subdomain = cds.context.user?.tokenInfo?.getPayload()?.ext_attr?.zdn; + const access_token = cache.get("SDM_ACCESS_TOKEN_"+subdomain); // to check if token exists if (access_token === undefined) { return new Promise(function (resolve, reject) { requests.requestClientCredentialsToken( - null, + subdomain, credentials.uaa, null, (error, response) => { @@ -90,7 +98,7 @@ async function getClientCredentialsToken(credentials) { ); reject(err); } else { - cache.set("SDM_ACCESS_TOKEN", response, 11*3600); //expires after 11 hours + cache.set("SDM_ACCESS_TOKEN_"+subdomain, response, 11*3600); //expires after 11 hours resolve(response); } } diff --git a/test/lib/util/index.test.js b/test/lib/util/index.test.js index 62858f0..5c47a0b 100644 --- a/test/lib/util/index.test.js +++ b/test/lib/util/index.test.js @@ -48,7 +48,17 @@ describe("util", () => { xssec.requests.requestUserToken.mockImplementation( (a, b, c, d, e, f, callback) => callback(null, dummyToken) ); - + cds.context = { + user: { + tokenInfo: { + getPayload: jest.fn(() => ({ + ext_attr: { + zdn: 'subdomain' // simulate the subdomain extraction + } + })), + }, + }, + }; const credentials = { uaa: "uaa" }; const req = { user: { @@ -57,11 +67,11 @@ describe("util", () => { }, }, }; - const accessToken = await fetchAccessToken(credentials, req.user.tokenInfo.getTokenValue); - expect(NodeCache.prototype.get).toBeCalledWith("example@example.com"); + const accessToken = await fetchAccessToken(credentials, req.user.tokenInfo.getTokenValue); + const expectedCacheKey = "example@example.com_subdomain"; expect(xssec.requests.requestUserToken).toBeCalled(); expect(NodeCache.prototype.set).toBeCalledWith( - "example@example.com", + expectedCacheKey, dummyToken, 11 * 3600 ); @@ -77,9 +87,20 @@ describe("util", () => { }, }, }; + cds.context = { + user: { + tokenInfo: { + getPayload: jest.fn(() => ({ + ext_attr: { + zdn: 'subdomain' // simulate the subdomain extraction + } + })), + }, + }, + }; const credentials = { uaa: "uaa" }; const accessToken = await fetchAccessToken(credentials, req.user.tokenInfo.getTokenValue); - expect(NodeCache.prototype.get).toBeCalledWith("example@example.com"); + expect(NodeCache.prototype.get).toBeCalledWith("example@example.com_subdomain"); expect(xssec.requests.requestUserToken).toBeCalled(); expect(accessToken).toBe(dummyToken); }); @@ -90,7 +111,17 @@ describe("util", () => { "email": "example@example.com", "exp": 2537353178 }; - + cds.context = { + user: { + tokenInfo: { + getPayload: jest.fn(() => ({ + ext_attr: { + zdn: 'subdomain' // simulate the subdomain extraction + } + })), + }, + }, + }; // Please replace 'your_secret_key' with your own secret key const secretKey = 'your_secret_key'; @@ -106,7 +137,7 @@ describe("util", () => { }; const credentials = { uaa: "uaa" }; const accessToken = await fetchAccessToken(credentials, req.user.tokenInfo.getTokenValue); - expect(NodeCache.prototype.get).toBeCalledWith("example@example.com"); + expect(NodeCache.prototype.get).toBeCalledWith("example@example.com_subdomain"); expect(xssec.requests.requestUserToken).not.toBeCalled(); expect(accessToken).toBe(dummyToken); }); @@ -120,6 +151,17 @@ describe("util", () => { (a, b, c, d, e, f, callback) => callback(new Error("test error"), { statusCode: 500 }) ); + cds.context = { + user: { + tokenInfo: { + getPayload: jest.fn(() => ({ + ext_attr: { + zdn: 'subdomain' // simulate the subdomain extraction + } + })), + }, + }, + }; const req = { user: { tokenInfo: { @@ -131,7 +173,7 @@ describe("util", () => { try { await fetchAccessToken(credentials, req.user.tokenInfo.getTokenValue); } catch (err) { - expect(NodeCache.prototype.get).toBeCalledWith("example@example.com"); + expect(NodeCache.prototype.get).toBeCalledWith("example@example.com_subdomain"); expect(xssec.requests.requestUserToken).toBeCalled(); expect(consoleErrorSpy).toBeCalledWith( "Response error while fetching access token 500" @@ -153,11 +195,21 @@ describe("util", () => { it('returns cached token if available', async () => { const cachedToken = 'mockedAccessToken'; NodeCache.prototype.get.mockImplementation(() => cachedToken); - + cds.context = { + user: { + tokenInfo: { + getPayload: jest.fn(() => ({ + ext_attr: { + zdn: 'subdomain' // simulate the subdomain extraction + } + })), + }, + }, + }; const token = await getClientCredentialsToken({ uaa: 'mockedUaa' }); expect(token).toBe(cachedToken); - expect(NodeCache.prototype.get).toHaveBeenCalledWith('SDM_ACCESS_TOKEN'); + expect(NodeCache.prototype.get).toHaveBeenCalledWith('SDM_ACCESS_TOKEN_subdomain'); expect(xssec.requests.requestClientCredentialsToken).not.toHaveBeenCalled(); }); @@ -168,13 +220,23 @@ describe("util", () => { xssec.requests.requestClientCredentialsToken.mockImplementation((_, __, ___, callback) => { callback(null, mockResponse); }); - + cds.context = { + user: { + tokenInfo: { + getPayload: jest.fn(() => ({ + ext_attr: { + zdn: 'subdomain' // simulate the subdomain extraction + } + })), + }, + }, + }; const token = await getClientCredentialsToken(credentials); expect(token).toBe(mockResponse); - expect(NodeCache.prototype.set).toHaveBeenCalledWith('SDM_ACCESS_TOKEN', mockResponse, expect.any(Number)); + expect(NodeCache.prototype.set).toHaveBeenCalledWith('SDM_ACCESS_TOKEN_subdomain', mockResponse, expect.any(Number)); expect(xssec.requests.requestClientCredentialsToken).toHaveBeenCalledWith( - null, + "subdomain", credentials.uaa, null, expect.any(Function)