Skip to content

Commit 8e5413e

Browse files
committed
Refactor AWS and Azure object store credential retrieval by centralizing validation logic and removing redundant service manager checks
1 parent 47700a6 commit 8e5413e

File tree

4 files changed

+118
-75
lines changed

4 files changed

+118
-75
lines changed

lib/aws-s3.js

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -103,31 +103,8 @@ module.exports = class AWSAttachmentsService extends require("./basic") {
103103
return
104104
}
105105

106-
// Validate Service Manager configuration
107-
const serviceManagerCreds = cds.env.requires?.serviceManager?.credentials
108-
if (!serviceManagerCreds) {
109-
logConfig.configValidation('serviceManager.credentials', serviceManagerCreds, false,
110-
'Bind a Service Manager instance for separate object store mode')
111-
throw new Error("Service Manager Instance is not bound")
112-
}
113-
114-
const { sm_url, url, clientid, clientsecret, certificate, key, certurl } = serviceManagerCreds
115-
116-
// Validate required Service Manager fields
117-
const requiredSmFields = ['sm_url', 'url', 'clientid']
118-
const missingSmFields = requiredSmFields.filter(field => !serviceManagerCreds[field])
119-
120-
if (missingSmFields.length > 0) {
121-
logConfig.configValidation('serviceManager.credentials', serviceManagerCreds, false,
122-
`Service Manager credentials missing: ${missingSmFields.join(', ')}`)
123-
throw new Error(`Missing Service Manager credentials: ${missingSmFields.join(', ')}`)
124-
}
125-
126-
logConfig.debug('Fetching access token for tenant', { tenantID, sm_url })
127-
const token = await utils.fetchToken(url, clientid, clientsecret, certificate, key, certurl)
128-
129106
logConfig.debug('Fetching object store credentials for tenant', { tenantID })
130-
const objectStoreCreds = await utils.getObjectStoreCredentials(tenantID, sm_url, token)
107+
const objectStoreCreds = await utils.getObjectStoreCredentials(tenantID)
131108

132109
if (!objectStoreCreds) {
133110
logConfig.withSuggestion('error',

lib/azure-blob-storage.js

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -95,31 +95,8 @@ module.exports = class AzureAttachmentsService extends require("./basic") {
9595
return
9696
}
9797

98-
// Validate Service Manager configuration
99-
const serviceManagerCreds = cds.env.requires?.serviceManager?.credentials
100-
if (!serviceManagerCreds) {
101-
logConfig.configValidation('serviceManager.credentials', serviceManagerCreds, false,
102-
'Bind a Service Manager instance for separate object store mode')
103-
throw new Error("Service Manager Instance is not bound")
104-
}
105-
106-
const { sm_url, url, clientid, clientsecret, certificate, key, certurl } = serviceManagerCreds
107-
108-
// Validate required Service Manager fields
109-
const requiredSmFields = ['sm_url', 'url', 'clientid']
110-
const missingSmFields = requiredSmFields.filter(field => !serviceManagerCreds[field])
111-
112-
if (missingSmFields.length > 0) {
113-
logConfig.configValidation('serviceManager.credentials', serviceManagerCreds, false,
114-
`Service Manager credentials missing: ${missingSmFields.join(', ')}`)
115-
throw new Error(`Missing Service Manager credentials: ${missingSmFields.join(', ')}`)
116-
}
117-
118-
logConfig.debug('Fetching access token for tenant', { tenantID, sm_url })
119-
const token = await utils.fetchToken(url, clientid, clientsecret, certificate, key, certurl)
120-
12198
logConfig.debug('Fetching object store credentials for tenant', { tenantID })
122-
const objectStoreCreds = await utils.getObjectStoreCredentials(tenantID, sm_url, token)
99+
const objectStoreCreds = await utils.getObjectStoreCredentials(tenantID)
123100

124101
if (!objectStoreCreds) {
125102
logConfig.withSuggestion('error',

lib/helper.js

Lines changed: 111 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,117 @@
11
const axios = require('axios')
22
const https = require("https")
33
const { logConfig } = require('./logger')
4+
const cds = require('@sap/cds')
5+
6+
/**
7+
* Validates the presence of required Service Manager credentials
8+
* @param {*} serviceManagerCreds - Service Manager credentials object
9+
* @throws Will throw an error if validation fails
10+
*/
11+
function validateServiceManagerCredentials(serviceManagerCreds) {
12+
if (!serviceManagerCreds) {
13+
logConfig.configValidation('serviceManager.credentials', serviceManagerCreds, false,
14+
'Bind a Service Manager instance for separate object store mode')
15+
throw new Error("Service Manager Instance is not bound")
16+
}
417

5-
async function getObjectStoreCredentials(tenantID, sm_url, token) {
6-
logConfig.processStep('Fetching object store credentials', { tenantID, sm_url })
18+
const requiredSmFields = ['sm_url', 'url', 'clientid']
19+
const missingSmFields = requiredSmFields.filter(field => !serviceManagerCreds[field])
20+
21+
if (missingSmFields.length > 0) {
22+
logConfig.configValidation('serviceManager.credentials', serviceManagerCreds, false,
23+
`Service Manager credentials missing: ${missingSmFields.join(', ')}`)
24+
throw new Error(`Missing Service Manager credentials: ${missingSmFields.join(', ')}`)
25+
}
26+
}
727

8-
// Validate inputs
28+
/**
29+
* Validates the inputs required for fetching object store credentials
30+
* @param {string} tenantID - Tenant ID
31+
* @param {string} sm_url - Service Manager URL
32+
* @param {string} token - Access token
33+
* @returns
34+
*/
35+
function validateInputs(tenantID, sm_url, token) {
936
if (!tenantID) {
1037
logConfig.withSuggestion('error', 'Tenant ID is required for object store credentials', null,
1138
'Ensure multitenancy is properly configured and tenant context is available', { tenantID })
12-
return null
39+
return false
1340
}
1441

1542
if (!sm_url) {
1643
logConfig.configValidation('serviceManager.credentials.sm_url', sm_url, false,
1744
'Bind a Service Manager instance to your application')
18-
return null
45+
return false
1946
}
2047

2148
if (!token) {
2249
logConfig.withSuggestion('error', 'Access token is required for Service Manager API', null,
2350
'Check if token fetching completed successfully', { hasToken: !!token })
51+
return false
52+
}
53+
54+
return true
55+
}
56+
57+
/**
58+
* Fetches object store service binding from Service Manager
59+
* @param {string} sm_url - Service Manager URL
60+
* @param {string} tenantID - Tenant ID
61+
* @param {string} token - Access token
62+
* @returns {Promise<Array>} - Promise resolving to array of service bindings
63+
*/
64+
async function fetchObjectStoreBinding(sm_url, tenantID, token) {
65+
logConfig.debug('Making Service Manager API call', {
66+
tenantID,
67+
endpoint: `${sm_url}/v1/service_bindings`,
68+
labelQuery: `service eq 'OBJECT_STORE' and tenant_id eq '${tenantID}'`
69+
})
70+
71+
const response = await axios.get(`${sm_url}/v1/service_bindings`, {
72+
params: { labelQuery: `service eq 'OBJECT_STORE' and tenant_id eq '${tenantID}'` },
73+
headers: {
74+
'Accept': 'application/json',
75+
'Authorization': `Bearer ${token}`,
76+
'Content-Type': 'application/json'
77+
}
78+
})
79+
80+
return response.data?.items || []
81+
}
82+
83+
/**
84+
* Retrieves object store credentials for a given tenant
85+
* @param {*} tenantID - Tenant ID
86+
* @returns {Promise<Object|null>} - Promise resolving to object store credentials or null
87+
*/
88+
async function getObjectStoreCredentials(tenantID) {
89+
const serviceManagerCreds = cds.env.requires?.serviceManager?.credentials
90+
91+
validateServiceManagerCredentials(serviceManagerCreds)
92+
93+
const { sm_url, url, clientid, clientsecret, certificate, key, certurl } = serviceManagerCreds
94+
95+
logConfig.debug('Fetching access token for tenant', { tenantID, sm_url: sm_url })
96+
const token = await fetchToken(url, clientid, clientsecret, certificate, key, certurl)
97+
98+
logConfig.processStep('Fetching object store credentials', { tenantID, sm_url })
99+
100+
if (!validateInputs(tenantID, sm_url, token)) {
24101
return null
25102
}
26103

27104
try {
28-
logConfig.debug('Making Service Manager API call', {
29-
tenantID,
30-
endpoint: `${sm_url}/v1/service_bindings`,
31-
labelQuery: `service eq 'OBJECT_STORE' and tenant_id eq '${tenantID}'`
32-
})
33-
34-
const response = await axios.get(`${sm_url}/v1/service_bindings`, {
35-
params: { labelQuery: `service eq 'OBJECT_STORE' and tenant_id eq '${tenantID}'` },
36-
headers: {
37-
'Accept': 'application/json',
38-
'Authorization': `Bearer ${token}`,
39-
'Content-Type': 'application/json'
40-
}
41-
})
105+
const items = await fetchObjectStoreBinding(sm_url, tenantID, token)
42106

43-
if (!response.data?.items?.length) {
107+
if (!items.length) {
44108
logConfig.withSuggestion('error', `No object store service binding found for tenant`, null,
45109
'Ensure an Object Store instance is subscribed and bound for this tenant',
46-
{ tenantID, itemsFound: response.data?.items?.length || 0 })
110+
{ tenantID, itemsFound: 0 })
47111
return null
48112
}
49113

50-
const credentials = response.data.items[0]
114+
const credentials = items[0]
51115
logConfig.info('Object store credentials retrieved successfully', {
52116
tenantID,
53117
hasCredentials: !!credentials,
@@ -72,6 +136,16 @@ async function getObjectStoreCredentials(tenantID, sm_url, token) {
72136
}
73137
}
74138

139+
/**
140+
* Fetches an OAuth token using either client credentials or MTLS
141+
* @param {string} url - Token endpoint URL
142+
* @param {string} clientid - Client ID
143+
* @param {string} clientsecret - Client Secret
144+
* @param {string} certificate - MTLS Certificate
145+
* @param {string} key - MTLS Key
146+
* @param {string} certURL - MTLS Certificate URL
147+
* @returns {Promise<string>} - Promise resolving to access token
148+
*/
75149
async function fetchToken(url, clientid, clientsecret, certificate, key, certURL) {
76150
logConfig.processStep('Determining token fetch method', {
77151
hasClientCredentials: !!(clientid && clientsecret),
@@ -106,6 +180,13 @@ async function fetchToken(url, clientid, clientsecret, certificate, key, certURL
106180
}
107181
}
108182

183+
/**
184+
* Fetches OAuth token using client credentials flow
185+
* @param {string} url - Token endpoint URL
186+
* @param {string} clientid - Client ID
187+
* @param {string} clientsecret - Client Secret
188+
* @returns
189+
*/
109190
async function fetchTokenWithClientSecret(url, clientid, clientsecret) {
110191
const startTime = Date.now()
111192

@@ -150,6 +231,14 @@ async function fetchTokenWithClientSecret(url, clientid, clientsecret) {
150231
}
151232
}
152233

234+
/**
235+
* Fetches OAuth token using MTLS authentication
236+
* @param {string} certURL - Certificate URL
237+
* @param {string} clientid - Client ID
238+
* @param {string} certificate - MTLS Certificate
239+
* @param {string} key - MTLS Key
240+
* @returns {Promise<string>} - Promise resolving to access token
241+
*/
153242
async function fetchTokenWithMTLS(certURL, clientid, certificate, key) {
154243
const startTime = Date.now()
155244

tests/unit/unitTests.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,28 +122,28 @@ describe('scanRequest', () => {
122122
describe('getObjectStoreCredentials', () => {
123123
it('should return credentials from service manager', async () => {
124124
axios.get.mockResolvedValue({ data: { items: [{ id: 'test-cred' }] } })
125-
const creds = await getObjectStoreCredentials('tenant', 'url', 'token')
125+
const creds = await getObjectStoreCredentials('tenant')
126126
expect(creds.id).toBe('test-cred')
127127
})
128128

129129
it('should return null when tenant ID is missing', async () => {
130-
const creds = await getObjectStoreCredentials(null, 'url', 'token')
130+
const creds = await getObjectStoreCredentials(null)
131131
expect(creds).toBeNull()
132132
})
133133

134134
it('should return null when sm_url is missing', async () => {
135-
const creds = await getObjectStoreCredentials('tenant', null, 'token')
135+
const creds = await getObjectStoreCredentials('tenant')
136136
expect(creds).toBeNull()
137137
})
138138

139139
it('should return null when token is missing', async () => {
140-
const creds = await getObjectStoreCredentials('tenant', 'url', null)
140+
const creds = await getObjectStoreCredentials('tenant')
141141
expect(creds).toBeNull()
142142
})
143143

144144
it('should handle error gracefully and return null', async () => {
145145
axios.get.mockRejectedValue(new Error('fail'))
146-
const creds = await getObjectStoreCredentials('tenant', 'url', 'token')
146+
const creds = await getObjectStoreCredentials('tenant')
147147
expect(creds).toBeNull()
148148
})
149149
})

0 commit comments

Comments
 (0)