Skip to content

Commit bd12d46

Browse files
authored
Integrate credentials checks in initializeCompute and startCompute handlers (#937)
* Integrate credentials checks for service and asset levels in initializeCompute and startCompute handlers. Added tests. * skip lint. * Update ci docker logs. * Fix lint. * Added credentials check for free start compute. Use download endpoint from policy server. * log error. * Check if did is provided on free start compute. * cleanup logs. * Integrate commands for compute from policy server. * Fix condition in the test. * Debug result. * cleanup. * print ddo test. * Updated test. * cleanup function. * remove initialize compute command. Update initializeCompute handler. * Enhance code. * Define dedicated type for policy server when it comes on the handler task. * Fix type of policy server on http requests. * Fix commands usage for policy server. * Fix arguments. * Fix review. * Use ddo.js for DDO fields manipulation. * Update for free start compute. * Update datasets samples. * Update branch for cli. * Fix datasets samples. * Fix samples and ci. * Update ddo.js. * Update lock. * Updated ddo schemas version. * Fix samples. * Add engines for test. * Bring all tests back.
1 parent 5c0f8ca commit bd12d46

15 files changed

Lines changed: 657 additions & 102 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ jobs:
163163
ASSET_PURGATORY_URL: 'https://raw.githubusercontent.com/oceanprotocol/list-purgatory/main/list-assets.json'
164164
ACCOUNT_PURGATORY_URL: 'https://raw.githubusercontent.com/oceanprotocol/list-purgatory/main/list-accounts.json'
165165
- name: docker logs
166-
run: docker logs ocean-ocean-contracts-1 && docker logs ocean-kindcluster-1 && docker logs ocean-computetodata-1 && docker logs ocean-typesense-1
166+
run: docker logs ocean-ocean-contracts-1 && docker logs ocean-typesense-1
167167
if: ${{ failure() }}
168168
- uses: actions/upload-artifact@v4
169169
with:
@@ -231,7 +231,7 @@ jobs:
231231
done
232232
233233
- name: docker logs
234-
run: docker logs ocean-ocean-contracts-1 && docker logs ocean-kindcluster-1 && docker logs ocean-computetodata-1 && docker logs ocean-typesense-1
234+
run: docker logs ocean-contracts-1 && docker logs ocean-typesense-1
235235
if: ${{ failure() }}
236236

237237
- name: Checkout Ocean Node

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"@libp2p/websockets": "^8.1.1",
7474
"@multiformats/multiaddr": "^10.2.0",
7575
"@oceanprotocol/contracts": "^2.3.0",
76-
"@oceanprotocol/ddo-js": "^0.1.1",
76+
"@oceanprotocol/ddo-js": "^0.1.2",
7777
"@types/lodash.clonedeep": "^4.5.7",
7878
"axios": "^1.8.4",
7979
"base58-js": "^2.0.0",

src/@types/commands.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
UrlFileObject,
1717
BaseFileObject
1818
} from './fileObject'
19+
import { PolicyServerTask } from './policyServer.js'
1920

2021
export interface Command {
2122
command: string // command name
@@ -58,7 +59,7 @@ export interface DownloadCommand extends Command {
5859
consumerAddress: string
5960
signature: string
6061
aes_encrypted_key?: string // if not present it means download without encryption
61-
policyServer?: any // object to pass to policy server
62+
policyServer?: PolicyServerTask // object to pass to policy server
6263
}
6364

6465
export interface FileInfoCommand extends Command {
@@ -138,7 +139,7 @@ export interface GetFeesCommand extends Command {
138139
serviceId: string
139140
consumerAddress?: string
140141
validUntil?: number // this allows a user to request a fee that is valid only for a limited period of time, less than service.timeout
141-
policyServer?: any // object to pass to policyServer
142+
policyServer?: PolicyServerTask // object to pass to policyServer
142143
}
143144
// admin commands
144145
export interface AdminStopNodeCommand extends AdminCommand {}
@@ -189,6 +190,7 @@ export interface ComputeInitializeCommand extends Command {
189190
consumerAddress: string
190191
signature?: string
191192
maxJobDuration: number
193+
policyServer?: PolicyServerTask // object to pass to policy server
192194
}
193195

194196
export interface FreeComputeStartCommand extends Command {
@@ -201,6 +203,7 @@ export interface FreeComputeStartCommand extends Command {
201203
output?: ComputeOutput
202204
resources?: ComputeResourceRequest[]
203205
maxJobDuration?: number
206+
policyServer?: PolicyServerTask // object to pass to policy server
204207
metadata?: DBComputeJobMetadata
205208
}
206209
export interface PaidComputeStartCommand extends FreeComputeStartCommand {

src/@types/policyServer.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,11 @@ export interface PolicyServerResult {
33
message?: string // error message, if any
44
httpStatus?: number // status returned by server
55
}
6+
7+
export interface PolicyServerTask {
8+
sessionId?: string
9+
successRedirectUri?: string
10+
errorRedirectUri?: string
11+
responseRedirectUri?: string
12+
presentationDefinitionUri?: string
13+
}

src/components/core/compute/initialize.ts

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ import {
2424
validateCommandParameters
2525
} from '../../httpRoutes/validateCommands.js'
2626
import { isAddress } from 'ethers'
27-
import { getConfiguration } from '../../../utils/index.js'
27+
import { getConfiguration, isPolicyServerConfigured } from '../../../utils/index.js'
2828
import { sanitizeServiceFiles } from '../../../utils/util.js'
2929
import { FindDdoHandler } from '../handler/ddoHandler.js'
3030
import { isOrderingAllowedForAsset } from '../handler/downloadHandler.js'
3131
import { getNonceAsNumber } from '../utils/nonceHandler.js'
3232
import { C2DEngineDocker, getAlgorithmImage } from '../../c2d/compute_engine_docker.js'
33-
import { DDOManager } from '@oceanprotocol/ddo-js'
33+
import { Credentials, DDOManager } from '@oceanprotocol/ddo-js'
34+
import { areKnownCredentialTypes, checkCredentials } from '../../../utils/credentials.js'
35+
import { PolicyServer } from '../../policyServer/index.js'
3436

3537
export class ComputeInitializeHandler extends CommandHandler {
3638
validate(command: ComputeInitializeCommand): ValidateParams {
@@ -178,11 +180,12 @@ export class ComputeInitializeHandler extends CommandHandler {
178180

179181
// check algo
180182
let index = 0
183+
const policyServer = new PolicyServer()
181184
for (const elem of [...[task.algorithm], ...task.datasets]) {
182185
const result: any = { validOrder: false }
183186
if ('documentId' in elem && elem.documentId) {
184187
result.did = elem.documentId
185-
result.serviceId = elem.documentId
188+
result.serviceId = elem.serviceId
186189
const ddo = await new FindDdoHandler(node).findAndFormatDdo(elem.documentId)
187190
if (!ddo) {
188191
const error = `DDO ${elem.documentId} not found`
@@ -194,6 +197,12 @@ export class ComputeInitializeHandler extends CommandHandler {
194197
}
195198
}
196199
}
200+
const ddoInstance = DDOManager.getDDOClass(ddo)
201+
const {
202+
chainId: ddoChainId,
203+
nftAddress,
204+
credentials
205+
} = ddoInstance.getDDOFields()
197206
const isOrdable = isOrderingAllowedForAsset(ddo)
198207
if (!isOrdable.isOrdable) {
199208
CORE_LOGGER.error(isOrdable.reason)
@@ -205,6 +214,39 @@ export class ComputeInitializeHandler extends CommandHandler {
205214
}
206215
}
207216
}
217+
// check credentials (DDO level)
218+
let accessGrantedDDOLevel: boolean
219+
if (credentials) {
220+
// if POLICY_SERVER_URL exists, then ocean-node will NOT perform any checks.
221+
// It will just use the existing code and let PolicyServer decide.
222+
if (isPolicyServerConfigured()) {
223+
const response = await policyServer.checkStartCompute(
224+
ddoInstance.getDid(),
225+
ddo,
226+
elem.serviceId,
227+
task.consumerAddress,
228+
task.policyServer
229+
)
230+
accessGrantedDDOLevel = response.success
231+
} else {
232+
accessGrantedDDOLevel = areKnownCredentialTypes(credentials as Credentials)
233+
? checkCredentials(credentials as Credentials, task.consumerAddress)
234+
: true
235+
}
236+
if (!accessGrantedDDOLevel) {
237+
CORE_LOGGER.logMessage(
238+
`Error: Access to asset ${ddoInstance.getDid()} was denied`,
239+
true
240+
)
241+
return {
242+
stream: null,
243+
status: {
244+
httpStatus: 403,
245+
error: `Error: Access to asset ${ddoInstance.getDid()} was denied`
246+
}
247+
}
248+
}
249+
}
208250
const service = AssetUtils.getServiceById(ddo, elem.serviceId)
209251
if (!service) {
210252
const error = `Cannot find service ${elem.serviceId} in DDO ${elem.documentId}`
@@ -216,9 +258,41 @@ export class ComputeInitializeHandler extends CommandHandler {
216258
}
217259
}
218260
}
261+
// check credentials on service level
262+
// if using a policy server and we are here it means that access was granted (they are merged/assessed together)
263+
if (service.credentials) {
264+
let accessGrantedServiceLevel: boolean
265+
if (isPolicyServerConfigured()) {
266+
// we use the previous check or we do it again
267+
// (in case there is no DDO level credentials and we only have Service level ones)
268+
const response = await policyServer.checkStartCompute(
269+
ddo.id,
270+
ddo,
271+
elem.serviceId,
272+
task.consumerAddress,
273+
task.policyServer
274+
)
275+
accessGrantedServiceLevel = accessGrantedDDOLevel || response.success
276+
} else {
277+
accessGrantedServiceLevel = areKnownCredentialTypes(service.credentials)
278+
? checkCredentials(service.credentials, task.consumerAddress)
279+
: true
280+
}
219281

220-
const ddoInstance = DDOManager.getDDOClass(ddo)
221-
const { chainId: ddoChainId, nftAddress } = ddoInstance.getDDOFields()
282+
if (!accessGrantedServiceLevel) {
283+
CORE_LOGGER.logMessage(
284+
`Error: Access to service with id ${service.id} was denied`,
285+
true
286+
)
287+
return {
288+
stream: null,
289+
status: {
290+
httpStatus: 403,
291+
error: `Error: Access to service with id ${service.id} was denied`
292+
}
293+
}
294+
}
295+
}
222296
const config = await getConfiguration()
223297
const { rpc, network, chainId, fallbackRPCs } =
224298
config.supportedNetworks[ddoChainId]

0 commit comments

Comments
 (0)