Skip to content

Commit

Permalink
Merge branch 'main' into update/main/zio
Browse files Browse the repository at this point in the history
  • Loading branch information
FabioPinheiro authored Sep 19, 2024
2 parents a92a2d7 + dd62433 commit 9a58f2a
Show file tree
Hide file tree
Showing 95 changed files with 1,140 additions and 352 deletions.
55 changes: 8 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,19 @@ In order to use the Cloud Agent, you establish a business logic controller respo

As a result, you can concentrate on crafting self-sovereign identity solutions using well-known web development tools, without the need to delve into the intricacies of lower-level cryptography and identity protocol internals.

## User documentation

All documentation, tutorials and API references for the Identus ecosystem can be found at [https://hyperledger.github.io/identus-docs/](https://hyperledger.github.io/identus-docs/)

## Features

* Rest API
* DIDComm V2
* W3C-compliant `did:prism` and `did:peer` methods
* Credential types
* JWT
* AnonCreds (coming soon)
* JWT-VC
* SD-JWT-VC
* AnonCreds
* HTTP events notification
* Cardano as a distributed ledger
* Secrets management with Hashicorp vault
Expand Down Expand Up @@ -77,17 +82,6 @@ The next diagrams offer a concise architectural overview, depicting a Cloud Agen
- SBT (latest version)
- Git (for cloning the repository)
- Docker (for running the PostgreSQL database, Hashicorp Vault, APISIX, and PRISM Node)
- [GITHUB_TOKEN](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) environment variable (required for SBT plugins and access to the GitHub packages)

#### Login to GitHub packages

To login to GitHub packages, you need to create a personal access token and set it as an environment variable together with your GitHub username. Here is an example of how you can do this:

```bash
export GITHUB_TOKEN=your-personal-access-token
export GITHUB_USER=your-github-username
docker login ghcr.io -u $GITHUB_USER -p $GITHUB_TOKEN
```

#### Compile, Test, create the docker image of the Cloud Agent

Expand Down Expand Up @@ -128,34 +122,7 @@ System requirements can vary depending on the use case. The following are the mi

#### Running locally in demo mode

Here is a general example of running a Cloud Agent locally:
```bash
PORT=${PORT} AGENT_VERSION=${AGENT_VERSION} PRISM_NODE_VERSION=${PRISM_NODE_VERSION} \
docker compose \
-p "${AGENT_ROLE}" \
-f ./infrastructure/shared/docker-compose-demo.yml \
up --wait
```

The `PORT` variable is used to specify the port number for the Cloud Agent to listen on. The `AGENT_VERSION` and `PRISM_NODE_VERSION` variables are used to specify the versions of the Cloud Agent and PRISM Node to use. The `AGENT_ROLE` variable is used to specify the role of the Cloud Agent. The `AGENT_ROLE` variable can be set to `issuer`, `verifier` or `holder`.

In real life, you will need to start at least two Cloud Agent instances with different roles. For example, you can start one instance with the `issuer` role and another one with the `holder` role. The `issuer` instance will be used to issue verifiable credentials (VCs) and the `holder` instance will be used to hold VCs. Here is an example of how you can do this:

```bash
PORT=8080 AGENT_VERSION=${AGENT_VERSION} PRISM_NODE_VERSION=2.3.0 \
docker compose \
-p "issuer" \
-f ./infrastructure/shared/docker-compose-demo.yml \
up --wait
```

```bash
PORT=8090 AGENT_VERSION=${AGENT_VERSION} PRISM_NODE_VERSION=2.3.0 \
docker compose \
-p "holder" \
-f ./infrastructure/shared/docker-compose-demo.yml \
up --wait
```
To run Identus locally you should follow the instructions in the [Quickstart guide](https://hyperledger.github.io/identus-docs/docs/quick-start/)

If the Cloud Agent is started successfully, all the running containers should achieve `Healthy` state, and Cloud Agent Rest API should be available at the specified port, for example:
* `http://localhost:8080/cloud-agent` for the `issuer` instance
Expand All @@ -167,8 +134,6 @@ $ curl http://localhost:8080/cloud-agent/_system/health
{"version":"1.19.1"}
```

> For more information about all available configuration parameters, please, check [Cloud Agent configuration](https://docs.atalaprism.io/docs/atala-prism/prism-cloud-agent/environment-variables) section at the documentation portal and edit the `docker-compose-demo.yml` file accordingly.
#### Compatibility between Cloud Agent and PRISM Node

There could be some incompatibilities between the most latest versions of Cloud Agent and PRISM Node. Please, use the following table to check the compatibility between the versions:
Expand All @@ -189,10 +154,6 @@ The following tutorials will help you get started with the Cloud Agent and issue
* [Issuing verifiable credentials (VCs)](https://docs.atalaprism.io/tutorials/credentials/issue)
* [Presenting VC proofs](https://docs.atalaprism.io/tutorials/credentials/present-proof)

## User documentation

All extended documentation, tutorials and API references for the Identus ecosystem can be found at <https://docs.atalaprism.io/>

## Contributing

Please read our [contributions guidelines](./CONTRIBUTING.md) and submit your PRs. We enforce [developer certificate of origin (DCO) commit signing](./DCO.md).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import org.hyperledger.identus.castor.core.model.did.{
UpdateDIDAction,
VerificationRelationship
}
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.UriOrJsonEndpoint
import org.hyperledger.identus.shared.models.Base64UrlString
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.{value, UriOrJsonEndpoint}
import org.hyperledger.identus.shared.models.{Base64UrlString, KeyId}
import org.hyperledger.identus.shared.utils.Traverse.*
import zio.*

Expand Down Expand Up @@ -122,7 +122,7 @@ private[castor] trait ProtoModelHelper {
extension (publicKey: PublicKey) {
def toProto: node_models.PublicKey = {
node_models.PublicKey(
id = publicKey.id,
id = publicKey.id.value,
usage = publicKey.purpose match {
case VerificationRelationship.Authentication => node_models.KeyUsage.AUTHENTICATION_KEY
case VerificationRelationship.AssertionMethod => node_models.KeyUsage.ISSUING_KEY
Expand All @@ -140,7 +140,7 @@ private[castor] trait ProtoModelHelper {
extension (internalPublicKey: InternalPublicKey) {
def toProto: node_models.PublicKey = {
node_models.PublicKey(
id = internalPublicKey.id,
id = internalPublicKey.id.value,
usage = internalPublicKey.purpose match {
case InternalKeyPurpose.Master => node_models.KeyUsage.MASTER_KEY
case InternalKeyPurpose.Revocation => node_models.KeyUsage.REVOCATION_KEY
Expand Down Expand Up @@ -313,13 +313,13 @@ private[castor] trait ProtoModelHelper {
} yield purpose match {
case purpose: VerificationRelationship =>
PublicKey(
id = publicKey.id,
id = KeyId(publicKey.id),
purpose = purpose,
publicKeyData = keyData
)
case purpose: InternalKeyPurpose =>
InternalPublicKey(
id = publicKey.id,
id = KeyId(publicKey.id),
purpose = purpose,
publicKeyData = keyData
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.hyperledger.identus.castor.core.model.did

import org.hyperledger.identus.shared.models.Base64UrlString
import org.hyperledger.identus.shared.models.{Base64UrlString, KeyId}

final case class PublicKey(
id: String,
id: KeyId,
purpose: VerificationRelationship,
publicKeyData: PublicKeyData
)
Expand All @@ -14,7 +14,7 @@ enum InternalKeyPurpose {
}

final case class InternalPublicKey(
id: String,
id: KeyId,
purpose: InternalKeyPurpose,
publicKeyData: PublicKeyData
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.hyperledger.identus.castor.core.model

import org.hyperledger.identus.shared.models.KeyId

package object error {

sealed trait DIDOperationError
Expand Down Expand Up @@ -27,7 +29,7 @@ package object error {
final case class TooManyDidPublicKeyAccess(limit: Int, access: Option[Int]) extends OperationValidationError
final case class TooManyDidServiceAccess(limit: Int, access: Option[Int]) extends OperationValidationError
final case class InvalidArgument(msg: String) extends OperationValidationError
final case class InvalidMasterKeyData(ids: Seq[String]) extends OperationValidationError
final case class InvalidMasterKeyData(ids: Seq[KeyId]) extends OperationValidationError
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.hyperledger.identus.castor.core.model.error.OperationValidationError
import org.hyperledger.identus.castor.core.util.DIDOperationValidator.Config
import org.hyperledger.identus.castor.core.util.Prelude.*
import org.hyperledger.identus.shared.crypto.Apollo
import org.hyperledger.identus.shared.models.KeyId
import zio.*

import scala.collection.immutable.ArraySeq
Expand Down Expand Up @@ -70,14 +71,14 @@ private object CreateOperationValidator extends BaseOperationValidator {
else Left(OperationValidationError.InvalidArgument("create operation must contain at least 1 master key"))
}

private def extractKeyIds(operation: PrismDIDOperation.Create): Seq[String] = operation.publicKeys.map {
private def extractKeyIds(operation: PrismDIDOperation.Create): Seq[KeyId] = operation.publicKeys.map {
case PublicKey(id, _, _) => id
case InternalPublicKey(id, _, _) => id
}

private def extractKeyData(
operation: PrismDIDOperation.Create
): Seq[(String, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
): Seq[(KeyId, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
operation.publicKeys.map {
case PublicKey(id, purpose, data) => (id, purpose, data)
case InternalPublicKey(id, purpose, data) => (id, purpose, data)
Expand Down Expand Up @@ -139,15 +140,15 @@ private object UpdateOperationValidator extends BaseOperationValidator {
)
}

private def extractKeyIds(operation: PrismDIDOperation.Update): Seq[String] = operation.actions.collect {
private def extractKeyIds(operation: PrismDIDOperation.Update): Seq[KeyId] = operation.actions.collect {
case UpdateDIDAction.AddKey(pk) => pk.id
case UpdateDIDAction.AddInternalKey(pk) => pk.id
case UpdateDIDAction.RemoveKey(id) => id
case UpdateDIDAction.RemoveKey(id) => KeyId(id)
}

private def extractKeyData(
operation: PrismDIDOperation.Update
): Seq[(String, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
): Seq[(KeyId, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
operation.actions.collect {
case UpdateDIDAction.AddKey(pk) => (pk.id, pk.purpose, pk.publicKeyData)
case UpdateDIDAction.AddInternalKey(pk) => (pk.id, pk.purpose, pk.publicKeyData)
Expand Down Expand Up @@ -182,12 +183,12 @@ private object DeactivateOperationValidator extends BaseOperationValidator {

private trait BaseOperationValidator {

type KeyIdExtractor[T] = T => Seq[String]
type KeyIdExtractor[T] = T => Seq[KeyId]
type ServiceIdExtractor[T] = T => Seq[String]
type ServiceTypeExtractor[T] = T => Seq[(String, ServiceType)]
type ServiceEndpointExtractor[T] = T => Seq[(String, ServiceEndpoint)]
type ContextExtractor[T] = T => Seq[Seq[String]]
type KeyDataExtractor[T] = T => Seq[(String, VerificationRelationship | InternalKeyPurpose, PublicKeyData)]
type KeyDataExtractor[T] = T => Seq[(KeyId, VerificationRelationship | InternalKeyPurpose, PublicKeyData)]

protected def validateMaxPublicKeysAccess[T <: PrismDIDOperation](
config: Config
Expand Down Expand Up @@ -266,7 +267,7 @@ private trait BaseOperationValidator {
keyIdExtractor: KeyIdExtractor[T]
): Either[OperationValidationError, Unit] = {
val ids = keyIdExtractor(operation)
val invalidIds = ids.filterNot(UriUtils.isValidUriFragment)
val invalidIds = ids.map(_.value).filterNot(UriUtils.isValidUriFragment)
if (invalidIds.isEmpty) Right(())
else
Left(
Expand All @@ -289,7 +290,7 @@ private trait BaseOperationValidator {
config: Config
)(operation: T, keyIdExtractor: KeyIdExtractor[T]): Either[OperationValidationError, Unit] = {
val ids = keyIdExtractor(operation)
val invalidIds = ids.filter(_.length > config.maxIdSize)
val invalidIds = ids.filter(_.value.length > config.maxIdSize)
if (invalidIds.isEmpty) Right(())
else
Left(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.hyperledger.identus.castor.core.model.did.{
VerificationRelationship
}
import org.hyperledger.identus.castor.core.util.GenUtils
import org.hyperledger.identus.shared.models.KeyId
import zio.*
import zio.test.*
import zio.test.Assertion.*
Expand All @@ -17,22 +18,22 @@ object W3CModelHelperSpec extends ZIOSpecDefault {

import W3CModelHelper.*

private def generateInternalPublicKey(id: String, purpose: InternalKeyPurpose = InternalKeyPurpose.Master) =
private def generateInternalPublicKey(id: KeyId, purpose: InternalKeyPurpose = InternalKeyPurpose.Master) =
GenUtils.internalPublicKey
.map(_.copy(id = id, purpose = purpose))
.runCollectN(1)
.map(_.head)

private def generatePublicKey(id: String, purpose: VerificationRelationship) =
private def generatePublicKey(id: KeyId, purpose: VerificationRelationship) =
GenUtils.publicKey.map(_.copy(id = id, purpose = purpose)).runCollectN(1).map(_.head)

private def generateService(id: String) =
GenUtils.service.map(_.copy(id = id)).runCollectN(1).map(_.head)

private def generateDIDData(
did: CanonicalPrismDID,
masterKeyId: String = "master-0",
keyIds: Seq[(String, VerificationRelationship)] = Seq.empty,
masterKeyId: KeyId = KeyId("master-0"),
keyIds: Seq[(KeyId, VerificationRelationship)] = Seq.empty,
serviceIds: Seq[String] = Seq.empty,
context: Seq[String] = Seq.empty
) =
Expand All @@ -49,11 +50,11 @@ object W3CModelHelperSpec extends ZIOSpecDefault {
didData <- generateDIDData(
did = did,
keyIds = Seq(
"auth-0" -> VerificationRelationship.Authentication,
"iss-0" -> VerificationRelationship.AssertionMethod,
"comm-0" -> VerificationRelationship.KeyAgreement,
"capinv-0" -> VerificationRelationship.CapabilityInvocation,
"capdel-0" -> VerificationRelationship.CapabilityDelegation
KeyId("auth-0") -> VerificationRelationship.Authentication,
KeyId("iss-0") -> VerificationRelationship.AssertionMethod,
KeyId("comm-0") -> VerificationRelationship.KeyAgreement,
KeyId("capinv-0") -> VerificationRelationship.CapabilityInvocation,
KeyId("capdel-0") -> VerificationRelationship.CapabilityDelegation
),
serviceIds = Seq("service-0")
)
Expand Down Expand Up @@ -92,7 +93,7 @@ object W3CModelHelperSpec extends ZIOSpecDefault {
longFormDID = PrismDID.buildLongFormFromOperation(PrismDIDOperation.Create(Nil, Nil, Nil))
didData <- generateDIDData(
did = did,
keyIds = Seq("auth-0" -> VerificationRelationship.Authentication)
keyIds = Seq(KeyId("auth-0") -> VerificationRelationship.Authentication)
)
didDoc = didData.toW3C(longFormDID)
} yield assert(didDoc.id)(equalTo(longFormDID.toString)) &&
Expand All @@ -104,7 +105,7 @@ object W3CModelHelperSpec extends ZIOSpecDefault {
did <- ZIO.fromEither(PrismDID.buildCanonicalFromSuffix("0" * 64))
didData <- generateDIDData(
did = did,
keyIds = Seq("auth-0" -> VerificationRelationship.Authentication),
keyIds = Seq(KeyId("auth-0") -> VerificationRelationship.Authentication),
serviceIds = Seq("service-0"),
context = Seq("user-defined-context")
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.hyperledger.identus.castor.core.service
import org.hyperledger.identus.castor.core.model.did.*
import org.hyperledger.identus.castor.core.model.error
import org.hyperledger.identus.shared.crypto.{Apollo, Secp256k1KeyPair}
import org.hyperledger.identus.shared.models.Base64UrlString
import org.hyperledger.identus.shared.models.{Base64UrlString, KeyId}
import zio.{mock, IO, URLayer, ZIO, ZLayer}
import zio.mock.{Expectation, Mock, Proxy}
import zio.test.Assertion
Expand Down Expand Up @@ -47,15 +47,15 @@ object MockDIDService extends Mock[DIDService] {
val createOperation = PrismDIDOperation.Create(
publicKeys = Seq(
InternalPublicKey(
id = "master-0",
id = KeyId("master-0"),
purpose = InternalKeyPurpose.Master,
publicKeyData = PublicKeyData.ECCompressedKeyData(
crv = EllipticCurve.SECP256K1,
data = Base64UrlString.fromByteArray(masterKeyPair.publicKey.getEncodedCompressed)
)
),
PublicKey(
id = "key-0",
id = KeyId("key-0"),
purpose = verificationRelationship,
publicKeyData = PublicKeyData.ECCompressedKeyData(
crv = EllipticCurve.SECP256K1,
Expand Down
Loading

0 comments on commit 9a58f2a

Please sign in to comment.