Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/DDO to Gaia-X Service Credential #595

Merged
merged 13 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions content/DDOtoServiceCredential/serviceCredentialForm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"metadata": {
"title": "Metadata",
"fields": [
{
"name": "didweb",
"label": "Service Provider DID:WEB ",
"placeholder": "did:web:yourdomain.eu",
"help": "The DID:WEB of the service provider / issuer publishing the Gaia-X Service Credential.",
"required": true
},
{
"name": "credentialHostingPath",
"label": "Service Credential Hosting Path ",
"placeholder": "https://yourdomain.eu/.well-known",
"help": "The base path to your public storage of Gaia-X Service Credentials.",
"required": true
},
{
"name": "pathToParticipantCredential",
"label": "Service Provider Participant Credential ",
"placeholder": "https://yourdomain.eu/.well-known/participant_vp.json",
"help": "URI of the service provider Gaia-X Participant Credential required to identity the responsible service provider.",
"required": true
},
{
"name": "knownDependencyCredentials",
"label": "Known Dependency Credentials",
"placeholder": "https://www.yourdomain.eu/.well-known/2210_gx_service_provider_1.json",
"help": "Contains resolvable links to the Gaia-X service credentials related to this service that can exist independently of it.",
"required": false
},
{
"name": "knownAggregatedServiceCredentials",
"label": "Known Aggregated Resource Credentials",
"placeholder": "https://www.yourdomain.eu/.well-known/2210_gx_resource_data_road_condition_7.json",
"help": "Contains resolveable links to Gaia-X Resource and Service Credentials related to this service that can exist independently of it.",
"required": false
}
]
},
"submission": {
"title": "Submit"
}
}
14 changes: 6 additions & 8 deletions src/@utils/downloadJSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@ export function isValidJSON(json: string): boolean {
}
}

// Download JSON file
export function downloadJSON(json: string, filename: string): void {
// check if the json is valid
if (isValidJSON(json)) {
oceanByte marked this conversation as resolved.
Show resolved Hide resolved
const element = document.createElement('a') // create an <a> tag
const element = document.createElement('a')
const file = new Blob([json], {
type: 'application/json'
}) // create a file
element.href = URL.createObjectURL(file) // create a URL for the file
element.download = `${filename}.json` // Set the file name
document.body.appendChild(element) // Append the tag to the body
element.click() // Click the a element
})
element.href = URL.createObjectURL(file)
element.download = `${filename}.json`
document.body.appendChild(element)
element.click()
}
}
24 changes: 24 additions & 0 deletions src/components/@shared/DDODownloadButton/_validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as Yup from 'yup'

export const initialValuesAsset = {
didweb: '',
credentialHostingPath: '',
pathToParticipantCredential: '',
knownDependencyCredentials: '',
knownAggregatedServiceCredentials: ''
}

export const validationAsset = Yup.object().shape({
didweb: Yup.string()
.matches(/^did:web:/, 'Invalid DID (only did:web is allowed)')
.required('Required'),
credentialHostingPath: Yup.string()
.url('Invalid URL')
.matches(/.*[^/]$/, 'URL must not end with /')
.required('Required'),
pathToParticipantCredential: Yup.string()
.url('Invalid URL')
.required('Required'),
knownDependencyCredentials: Yup.string().optional().url('Invalid URL'),
knownAggregatedServiceCredentials: Yup.string().optional().url('Invalid URL')
})
73 changes: 73 additions & 0 deletions src/components/@shared/DDODownloadButton/createJSON.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { downloadJSON } from '@utils/downloadJSON'

export interface DDOData {
id: string
services: { timeout: string }[]
}

interface FormData {
didweb: string
credentialHostingPath: string
pathToParticipantCredential: string
dependencyCredentialsList: { id: string }[]
serviceCredentialList: { id: string }[]
}

function getDomain(url: string) {
url = url.split('/').splice(0, 3).join('/')
return url
}

export function createServiceCredential(asset: DDOData, formData: FormData) {
const filename = `service_did_op_${asset.id.split(':')[2]}` // to get only the id without did:op:
const metadata = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://w3id.org/security/suites/jws-2020/v1',
'https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#'
],
type: 'VerifiableCredential',
id: `${formData.credentialHostingPath}/${filename}.json`,
issuer: formData.didweb,
issuanceDate: '',
credentialSubject: {
id: asset.id,
type: 'gx:ServiceOffering',
'gx:providedBy': {
id: formData.pathToParticipantCredential
},
'gx:maintainedBy': {
id: formData.pathToParticipantCredential
},
'gx:serviceOffering:serviceModel': 'subscription',
'gx:serviceOffering:subscriptionDuration':
asset.services[0].timeout || 'unlimited',
'gx:policy': `${formData.credentialHostingPath}/yourpolicy.json`,
'gx:termsAndConditions': {
'gx:URL': '[basedomain]/yourtermsandconditions.txt',
'gx:hash': '[hash]'
},
'gx:dataAccountExport': {
'gx:requestType': 'email',
'gx:accessType': 'digital',
'gx:formatType': 'mime/json'
},
'gx:serviceOffering:dataProtectionRegime': ['GDPR2016'],
'gx:serviceOffering:gdpr': [
{
'gx:serviceOffering:imprint': `${getDomain(
formData.credentialHostingPath
)}/imprint/`
},
{
'gx:serviceOffering:privacyPolicy': `${getDomain(
formData.credentialHostingPath
)}/privacy/`
}
],
'gx:dependsOn': formData.dependencyCredentialsList || [],
'gx:aggregationOf': formData.serviceCredentialList || []
}
}
downloadJSON(JSON.stringify(metadata), filename)
}
18 changes: 18 additions & 0 deletions src/components/@shared/DDODownloadButton/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.credential_margin_bottom {
margin-bottom: calc(var(--spacer) * var(--line-height));
}
.credential_input {
width: 70%;
}

.credential_input_div {
display: flex;
justify-content: space-between;
align-items: flex-end;
flex-wrap: wrap;
width: 100%;
}

.button {
max-height: 70px;
}
122 changes: 111 additions & 11 deletions src/components/@shared/DDODownloadButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,120 @@
import { ReactElement } from 'react'
import { ReactElement, useState } from 'react'
import Button from '../atoms/Button'
import { downloadJSON } from '@utils/downloadJSON'
import Modal from '../atoms/Modal'
import Input from '../FormInput'
import { createServiceCredential, DDOData } from './createJSON'
import { Field, Form, Formik } from 'formik'
import { getFieldContent } from '@utils/form'
import content from '../../../../content/DDOtoServiceCredential/serviceCredentialForm.json'
import { initialValuesAsset, validationAsset } from './_validation'
import InputWithList from './inputWithList'
import styles from './index.module.css'

interface values {
didweb: string
credentialHostingPath: string
pathToParticipantCredential: string
knownDependencyCredentials: string
knownAggregatedServiceCredentials: string
}

export default function DDODownloadButton({
asset
}: {
asset: any
asset: DDOData
}): ReactElement {
const [openModal, setOpenModal] = useState(false)
const [serviceCredentialList, setServiceCredentialList] = useState<
{ id: string }[]
>([])
const [dependencyCredentialsList, setDependencyCredentialsList] = useState<
{ id: string }[]
>([])
const clearLists = () => {
setOpenModal(false)
setServiceCredentialList([])
setDependencyCredentialsList([])
}
const handleSubmit = (values: values) => {
const formData = {
...values,
serviceCredentialList,
dependencyCredentialsList
}
createServiceCredential(asset, formData)
clearLists()
oceanByte marked this conversation as resolved.
Show resolved Hide resolved
}

return (
<Button
onClick={() =>
downloadJSON(JSON.stringify(asset), `${asset.metadata.name}_metadata`)
}
size="small"
>
Download DDO
</Button>
<>
<Button
className={styles.button}
onClick={() => setOpenModal(true)}
size="small"
>
Prepare Service Credential
</Button>

<Modal
title="Prepare Gaia-X Service Credential"
isOpen={openModal}
onToggleModal={() => {
clearLists()
}}
>
<p>
This manual export functionality will assist you to create Gaia-X
Service Credentials for this service which can be added to this
service for verification against the&nbsp;
<Button
style="text"
href={'https://docs.gaia-x.eu/framework/?tab=clearing-house'}
>
Gaia-X Digital Clearing Houses (GXDCH)
</Button>
. Credentials should be signed and hosted by the service provider.
</p>
<Formik
initialValues={initialValuesAsset}
validationSchema={validationAsset}
onSubmit={handleSubmit}
>
<Form>
<Field
{...getFieldContent('didweb', content.metadata.fields)}
component={Input}
name="didweb"
/>
<Field
{...getFieldContent(
'credentialHostingPath',
content.metadata.fields
)}
component={Input}
name="credentialHostingPath"
/>
<Field
{...getFieldContent(
'pathToParticipantCredential',
content.metadata.fields
)}
component={Input}
name="pathToParticipantCredential"
/>
<InputWithList
fieldname="knownDependencyCredentials"
setList={setDependencyCredentialsList}
list={dependencyCredentialsList}
/>
<InputWithList
fieldname="knownAggregatedServiceCredentials"
setList={setServiceCredentialList}
list={serviceCredentialList}
/>
<Button type="submit">Download</Button>
</Form>
</Formik>
</Modal>
</>
)
}
Loading
Loading