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: Add KID to the credential-offers API - ATL-7704 #1320

Merged
merged 3 commits into from
Sep 18, 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
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import org.hyperledger.identus.castor.core.model.did.{
UpdateDIDAction,
VerificationRelationship
}
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.value
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.UriOrJsonEndpoint
import org.hyperledger.identus.shared.models.Base64UrlString
import org.hyperledger.identus.shared.models.KeyId
import org.hyperledger.identus.shared.utils.Traverse.*
import zio.*

Expand Down Expand Up @@ -122,7 +124,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 +142,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 +315,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,10 @@
package org.hyperledger.identus.castor.core.model.did

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

final case class PublicKey(
id: String,
id: KeyId,
purpose: VerificationRelationship,
publicKeyData: PublicKeyData
)
Expand All @@ -14,7 +15,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 @@ -4,6 +4,7 @@ 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.KeyId
import zio.{mock, IO, URLayer, ZIO, ZLayer}
import zio.mock.{Expectation, Mock, Proxy}
import zio.test.Assertion
Expand Down Expand Up @@ -47,15 +48,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
Loading