Skip to content

Commit

Permalink
Merge pull request #890 from 0xPolygonID/add_default_credential_status
Browse files Browse the repository at this point in the history
chore: add default credential status
  • Loading branch information
martinsaporiti authored Jan 13, 2025
2 parents 2e490f8 + ff08e44 commit d5b9cac
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 57 deletions.
27 changes: 26 additions & 1 deletion internal/api/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"slices"
"strings"
"time"

Expand Down Expand Up @@ -76,7 +77,7 @@ func (s *Server) CreateCredential(ctx context.Context, request CreateCredentialR
}

var credentialStatusType *verifiable.CredentialStatusType
credentialStatusType, err = validateStatusType((*string)(request.Body.CredentialStatusType))
credentialStatusType, err = s.validateStatusType(ctx, did, (*string)(request.Body.CredentialStatusType))
if err != nil {
return CreateCredential400JSONResponse{N400JSONResponse{Message: err.Error()}}, nil
}
Expand Down Expand Up @@ -349,6 +350,30 @@ func (s *Server) GetCredentialOffer(ctx context.Context, request GetCredentialOf
}, nil
}

// validateStatusType - validate credential status type.
// If credentialStatusTypeRequest is nil or empty, it will return the credential status type from the auth claim (first non revoked).
func (s *Server) validateStatusType(ctx context.Context, did *w3c.DID, credentialStatusTypeRequest *string) (*verifiable.CredentialStatusType, error) {
var credentialStatusType verifiable.CredentialStatusType
if credentialStatusTypeRequest != nil && *credentialStatusTypeRequest != "" {
allowedCredentialStatuses := []string{string(verifiable.Iden3commRevocationStatusV1), string(verifiable.Iden3ReverseSparseMerkleTreeProof), string(verifiable.Iden3OnchainSparseMerkleTreeProof2023)}
if !slices.Contains(allowedCredentialStatuses, *credentialStatusTypeRequest) {
return nil, fmt.Errorf("Invalid Credential Status Type '%s'. Allowed Iden3commRevocationStatusV1.0, Iden3ReverseSparseMerkleTreeProof or Iden3OnchainSparseMerkleTreeProof2023.", *credentialStatusTypeRequest)
}
credentialStatusType = (verifiable.CredentialStatusType)(*credentialStatusTypeRequest)
} else {
credentialStatusType = verifiable.Iden3commRevocationStatusV1
authClaim, _ := s.claimService.GetAuthClaim(ctx, did)
if authClaim != nil {
credentialStatus, _ := authClaim.GetCredentialStatus()

if credentialStatus != nil {
credentialStatusType = credentialStatus.Type
}
}
}
return &credentialStatusType, nil
}

func toVerifiableRefreshService(s *RefreshService) *verifiable.RefreshService {
if s == nil {
return nil
Expand Down
69 changes: 32 additions & 37 deletions internal/api/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,20 +559,23 @@ func TestServer_DeleteCredential(t *testing.T) {
}

func TestServer_GetCredentialQrCode(t *testing.T) {
idStr := "did:polygonid:polygon:mumbai:2qPrv5Yx8s1qAmEnPym68LfT7gTbASGampiGU7TseL"
const (
method = "polygonid"
blockchain = "polygon"
network = "amoy"
BJJ = "BJJ"
)
idNoClaims := "did:polygonid:polygon:mumbai:2qGjTUuxZKqKS4Q8UmxHUPw55g15QgEVGnj6Wkq8Vk"
identity := &domain.Identity{
Identifier: idStr,
}

fixture := repositories.NewFixture(storage)
fixture.CreateIdentity(t, identity)
claim := fixture.NewClaim(t, identity.Identifier)
fixture.CreateClaim(t, claim)

server := newTestServer(t, nil)
handler := getHandler(context.Background(), server)

identity, err := server.Services.identity.Create(context.Background(), "http://localhost:3001", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
assert.NoError(t, err)

claim := fixture.NewClaim(t, identity.Identifier)
fixture.CreateClaim(t, claim)

type expected struct {
response GetCredentialOfferResponseObject
httpCode int
Expand All @@ -591,7 +594,7 @@ func TestServer_GetCredentialQrCode(t *testing.T) {
{
name: "No auth",
auth: authWrong,
did: idStr,
did: identity.Identifier,
claim: claim.ID,
expected: expected{
httpCode: http.StatusUnauthorized,
Expand All @@ -600,7 +603,7 @@ func TestServer_GetCredentialQrCode(t *testing.T) {
{
name: "should get an error non existing claimID",
auth: authOk,
did: idStr,
did: identity.Identifier,
claim: uuid.New(),
expected: expected{
response: GetCredentialOffer404JSONResponse{N404JSONResponse{
Expand Down Expand Up @@ -636,7 +639,7 @@ func TestServer_GetCredentialQrCode(t *testing.T) {
{
name: "Happy path, no type",
auth: authOk,
did: idStr,
did: identity.Identifier,
claim: claim.ID,
qrType: nil,
expected: expected{
Expand All @@ -648,7 +651,7 @@ func TestServer_GetCredentialQrCode(t *testing.T) {
{
name: "Happy path, type universalLink",
auth: authOk,
did: idStr,
did: identity.Identifier,
claim: claim.ID,
qrType: common.ToPointer("universalLink"),
expected: expected{
Expand All @@ -660,7 +663,7 @@ func TestServer_GetCredentialQrCode(t *testing.T) {
{
name: "Happy path, type deeplink",
auth: authOk,
did: idStr,
did: identity.Identifier,
claim: claim.ID,
qrType: common.ToPointer("deepLink"),
expected: expected{
Expand All @@ -672,7 +675,7 @@ func TestServer_GetCredentialQrCode(t *testing.T) {
{
name: "Happy path, type raw",
auth: authOk,
did: idStr,
did: identity.Identifier,
claim: claim.ID,
qrType: common.ToPointer("raw"),
expected: expected{
Expand Down Expand Up @@ -740,29 +743,21 @@ func TestServer_GetCredentialQrCode(t *testing.T) {

func TestServer_GetCredential(t *testing.T) {
server := newTestServer(t, nil)
idStr := "did:polygonid:polygon:mumbai:2qLduMv2z7hnuhzkcTWesCUuJKpRVDEThztM4tsJUj"
idStrWithoutClaims := "did:polygonid:polygon:mumbai:2qGjTUuxZKqKS4Q8UmxHUPw55g15QgEVGnj6Wkq8Vk"
identity := &domain.Identity{
Identifier: idStr,
}
fixture := repositories.NewFixture(storage)
fixture.CreateIdentity(t, identity)
const (
method = "polygonid"
blockchain = "polygon"
network = "amoy"
BJJ = "BJJ"
)

identity, err := server.Services.identity.Create(context.Background(), "http://localhost:3001", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
assert.NoError(t, err)

claim := fixture.NewClaim(t, identity.Identifier)
claim.MtProof = true
fixture.CreateClaim(t, claim)

query := repositories.ExecQueryParams{
Query: `INSERT INTO identity_mts (identifier, type) VALUES
($1, 0),
($1, 1),
($1, 2),
($1, 3)`,
Arguments: []interface{}{idStr},
}

fixture.ExecQuery(t, query)

handler := getHandler(context.Background(), server)

type expected struct {
Expand All @@ -782,15 +777,15 @@ func TestServer_GetCredential(t *testing.T) {
{
name: "No auth header",
auth: authWrong,
did: idStr,
did: identity.Identifier,
expected: expected{
httpCode: http.StatusUnauthorized,
},
},
{
name: "should get an error non existing claimID",
auth: authOk,
did: idStr,
did: identity.Identifier,
claimID: uuid.New(),
expected: expected{
httpCode: http.StatusNotFound,
Expand Down Expand Up @@ -826,7 +821,7 @@ func TestServer_GetCredential(t *testing.T) {
{
name: "should get the credentials",
auth: authOk,
did: idStr,
did: identity.Identifier,
claimID: claim.ID,
expected: expected{
httpCode: http.StatusOK,
Expand All @@ -839,7 +834,7 @@ func TestServer_GetCredential(t *testing.T) {
Type: "JsonSchemaValidator2018",
},
CredentialStatus: verifiable.CredentialStatus{
ID: fmt.Sprintf("http://localhost/v2/%s/credentials/revocation/status/%d", idStr, claim.RevNonce),
ID: fmt.Sprintf("http://localhost/v2/%s/credentials/revocation/status/%d", identity.Identifier, claim.RevNonce),
Type: "SparseMerkleTreeProof",
RevocationNonce: uint64(claim.RevNonce),
},
Expand All @@ -851,7 +846,7 @@ func TestServer_GetCredential(t *testing.T) {
},
ID: fmt.Sprintf("http://localhost/api/v2/credentials/%s", claim.ID),
IssuanceDate: common.ToPointer(time.Now()),
Issuer: idStr,
Issuer: identity.Identifier,
Type: []string{"VerifiableCredential", "KYCAgeCredential"},
RefreshService: &verifiable.RefreshService{
ID: "https://refresh-service.xyz",
Expand Down
18 changes: 18 additions & 0 deletions internal/api/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,24 @@ func (s *Server) GetIdentities(ctx context.Context, request GetIdentitiesRequest
credStatusType := GetIdentitiesResponseCredentialStatusType(credentialStatus.Type)
authBjjCredStatus = &credStatusType
}
} else {
// this case handle eth identity without published auth claim
authClaim, err := s.claimService.GetFirstNonRevokedAuthClaim(ctx, did)
if err != nil {
log.Error(ctx, "get identities. Getting first non revoked auth claim", "err", err)
return GetIdentities500JSONResponse{N500JSONResponse{
Message: err.Error(),
}}, nil
}
credentialStatus, err := authClaim.GetCredentialStatus()
if err != nil {
log.Error(ctx, "get identities. Getting credential status", "err", err)
return GetIdentities500JSONResponse{N500JSONResponse{
Message: err.Error(),
}}, nil
}
credStatusType := GetIdentitiesResponseCredentialStatusType(credentialStatus.Type)
authBjjCredStatus = &credStatusType
}

items := strings.Split(identity.Identifier, ":")
Expand Down
49 changes: 30 additions & 19 deletions internal/api/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ import (
"github.com/stretchr/testify/require"

"github.com/polygonid/sh-id-platform/internal/common"
"github.com/polygonid/sh-id-platform/internal/core/domain"
"github.com/polygonid/sh-id-platform/internal/core/ports"
"github.com/polygonid/sh-id-platform/internal/db/tests"
"github.com/polygonid/sh-id-platform/internal/kms"
"github.com/polygonid/sh-id-platform/internal/repositories"
)

func TestServer_CreateIdentity(t *testing.T) {
Expand Down Expand Up @@ -275,14 +273,24 @@ func TestServer_CreateIdentity(t *testing.T) {
}

func TestServer_GetIdentities(t *testing.T) {
const (
method = "polygonid"
blockchain = "polygon"
network = "amoy"
BJJ = "BJJ"
ETH = "ETH"
)
server := newTestServer(t, nil)
handler := getHandler(context.Background(), server)

identity1 := &domain.Identity{Identifier: "did:polygonid:polygon:mumbai:2qE1ZT16aqEWhh9mX9aqM2pe2ZwV995dTkReeKwCaQ"}
identity2 := &domain.Identity{Identifier: "did:polygonid:polygon:mumbai:2qMHFTHn2SC3XkBEJrR4eH4Yk8jRGg5bzYYG1ZGECa"}
fixture := repositories.NewFixture(storage)
fixture.CreateIdentity(t, identity1)
fixture.CreateIdentity(t, identity2)
identity1, err := server.Services.identity.Create(context.Background(), "http://localhost:3001", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
assert.NoError(t, err)

identity2, err := server.Services.identity.Create(context.Background(), "http://localhost:3001", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
assert.NoError(t, err)

identity3, err := server.Services.identity.Create(context.Background(), "http://localhost:3001", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: ETH})
assert.NoError(t, err)

type expected struct {
httpCode int
Expand Down Expand Up @@ -321,7 +329,13 @@ func TestServer_GetIdentities(t *testing.T) {
var response GetIdentities200JSONResponse
assert.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response))
assert.Equal(t, tc.expected.httpCode, rr.Code)
assert.True(t, len(response) >= 2)
assert.True(t, len(response) >= 3)
assert.Equal(t, identity1.Identifier, response[len(response)-3].Identifier)
assert.NotNil(t, response[len(response)-3].CredentialStatusType)
assert.Equal(t, identity2.Identifier, response[len(response)-2].Identifier)
assert.NotNil(t, response[len(response)-2].CredentialStatusType)
assert.Equal(t, identity3.Identifier, response[len(response)-1].Identifier)
assert.NotNil(t, response[len(response)-1].CredentialStatusType)
}
})
}
Expand Down Expand Up @@ -423,18 +437,15 @@ func TestServer_UpdateIdentity(t *testing.T) {
server := newTestServer(t, nil)
handler := getHandler(context.Background(), server)

identity := &domain.Identity{Identifier: "did:polygonid:polygon:amoy:2qQ8S2VKdQv7xYgzCn7KW2xgzUWrTRQjoZDYavJHBq"}
fixture := repositories.NewFixture(storage)
fixture.CreateIdentity(t, identity)
const (
method = "polygonid"
blockchain = "polygon"
network = "amoy"
BJJ = "BJJ"
)

state := domain.IdentityState{
Identifier: identity.Identifier,
State: common.ToPointer("state"),
Status: domain.StatusCreated,
ModifiedAt: time.Now(),
CreatedAt: time.Now(),
}
fixture.CreateIdentityStatus(t, state)
identity, err := server.Services.identity.Create(context.Background(), "http://localhost:3001", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
assert.NoError(t, err)

type expected struct {
httpCode int
Expand Down
1 change: 1 addition & 0 deletions internal/core/ports/claim_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ type ClaimService interface {
GetCredentialQrCode(ctx context.Context, issID *w3c.DID, id uuid.UUID, hostURL string) (*GetCredentialQrCodeResponse, error)
Agent(ctx context.Context, req *AgentRequest, mediatype iden3comm.MediaType) (*domain.Agent, error)
GetAuthClaim(ctx context.Context, did *w3c.DID) (*domain.Claim, error)
GetFirstNonRevokedAuthClaim(ctx context.Context, did *w3c.DID) (*domain.Claim, error)
GetAuthClaimForPublishing(ctx context.Context, did *w3c.DID, state string) (*domain.Claim, error)
UpdateClaimsMTPAndState(ctx context.Context, currentState *domain.IdentityState) error
Delete(ctx context.Context, issuerDID *w3c.DID, id uuid.UUID) error
Expand Down
14 changes: 14 additions & 0 deletions internal/core/services/claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,20 @@ func (c *claim) GetAuthClaim(ctx context.Context, did *w3c.DID) (*domain.Claim,
return c.icRepo.FindOneClaimBySchemaHash(ctx, c.storage.Pgx, did, string(authHash))
}

// GetFirstNonRevokedAuthClaim returns the first non-revoked authentication claim for the given DID. The AuthClaim may not be published
func (c *claim) GetFirstNonRevokedAuthClaim(ctx context.Context, did *w3c.DID) (*domain.Claim, error) {
authHash, err := core.AuthSchemaHash.MarshalText()
if err != nil {
return nil, err
}
authClaims, err := c.icRepo.GetAuthCoreClaims(ctx, c.storage.Pgx, did, string(authHash))
if err != nil {
return nil, err
}

return authClaims[0], nil
}

func (c *claim) GetAll(ctx context.Context, did w3c.DID, filter *ports.ClaimsFilter) ([]*domain.Claim, uint, error) {
claims, total, err := c.icRepo.GetAllByIssuerID(ctx, c.storage.Pgx, did, filter)
if err != nil {
Expand Down

0 comments on commit d5b9cac

Please sign in to comment.