diff --git a/devops/generate_proto_files.py b/devops/generate_proto_files.py new file mode 100644 index 000000000..f60a24aa9 --- /dev/null +++ b/devops/generate_proto_files.py @@ -0,0 +1,98 @@ +import glob +import os +import shutil +from os.path import abspath, join, dirname +from typing import List, Dict + + +def get_language_dir(language_name: str) -> str: + return abspath(join(dirname(abspath(__file__)), '..', language_name)) + + +def get_proto_files(dir_name: str = None) -> List[str]: + dir_name = dir_name or get_language_dir('proto') + proto_search_glob = join(dir_name, '**', '*.proto') + return [abspath(file_path) for file_path in glob.glob(proto_search_glob, recursive=True)] + + +def replace_lines_in_file(file_name: str, replace_lines: Dict[str, str]) -> None: + with open(file_name, 'r') as fid: + file_lines = fid.readlines() + + file_lines = [line.replace(key, value) for key, value in replace_lines.items() for line in file_lines] + + with open(file_name, 'w') as fid: + fid.writelines(file_lines) + + +def clean_proto_dir(language_proto_dir: str) -> None: + try: + shutil.rmtree(language_proto_dir) + except: + pass + os.mkdir(language_proto_dir) + + +def join_args(args: Dict[str, str]) -> str: + return " ".join([f'--{key}="{value}"' for (key, value) in args.items()]) if args else '' + + +def run_protoc(language_options: Dict[str, str] = None, + custom_options: Dict[str, str] = None, + proto_files: List[str] = None, + plugin: str = None, + protoc_executable: str = 'protoc') -> None: + language_arg_string = join_args(language_options) + proto_path_string = f'--proto_path="{get_language_dir("proto")}"' + custom_options_string = join_args(custom_options) + proto_files_string = " ".join(proto_files) + plugin_string = f'--plugin={plugin}' if plugin else '' + protoc_command = f'{protoc_executable} {plugin_string} {proto_path_string} {language_arg_string} {custom_options_string} {proto_files_string}' + print(protoc_command) + if os.system(protoc_command) != 0: + raise Exception("Protoc failed") + + +def update_golang(): + go_path = get_language_dir('go') + go_proto_path = join(go_path, 'proto') + clean_proto_dir(go_proto_path) + run_protoc({'go_out': go_proto_path, 'go-grpc_out': go_proto_path}, + {'go_opt': 'module=github.com/trinsic-id/sdk', 'go-grpc_opt': 'module=github.com/trinsic-id/sdk'}, + get_proto_files()) + # Remove okapi proto folder + shutil.rmtree(join(go_proto_path, 'go')) + # find and replace the sdk proto with okapi proto + replace_pairs = {'okapiproto "github.com/trinsic-id/sdk/go/okapiproto"': + 'okapiproto "github.com/trinsic-id/okapi/go/okapiproto"'} + for file_name in glob.glob(join(go_proto_path, '*.go')): + replace_lines_in_file(file_name, replace_pairs) + + +def update_ruby(): + ruby_path = get_language_dir('ruby') + ruby_proto_path = join(ruby_path, 'lib') + # TODO - clean selectively + run_protoc({'ruby_out': ruby_proto_path, 'grpc_out': ruby_proto_path}, {}, get_proto_files(), + protoc_executable='grpc_tools_ruby_protoc') + # TODO - Ruby type specifications + + +def update_java(): + java_path = get_language_dir('java') + java_proto_path = join(java_path, 'src', 'main', 'java') + # TODO - clean_proto_dir(java_proto_path) + run_protoc({'java_out': java_proto_path}, {}, get_proto_files(), + plugin=r"C:\bin\protoc-gen-grpc-java-1.39.0-windows-x86_64.exe") + # remove okapi pbmse + shutil.rmtree(join(java_proto_path, 'trinsic', 'okapi')) + + +def main(): + update_golang() + update_ruby() + update_java() + + +if __name__ == "__main__": + main() diff --git a/go/go.mod b/go/go.mod index e4d2edeec..faf913758 100644 --- a/go/go.mod +++ b/go/go.mod @@ -4,9 +4,8 @@ go 1.16 require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/protobuf v1.5.2 github.com/stretchr/testify v1.7.0 - github.com/trinsic-id/okapi/go v0.0.0-20211022163741-691fe8721335 + github.com/trinsic-id/okapi/go v0.0.0-20211022175546-f12ecb2812cb google.golang.org/grpc v1.41.0 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go/go.sum b/go/go.sum index ea01a0a52..407ac4a32 100644 --- a/go/go.sum +++ b/go/go.sum @@ -78,6 +78,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/trinsic-id/okapi/go v0.0.0-20211022163741-691fe8721335 h1:rK/tJLP611yLpYgnoxeVHVLUoSXncym90WMnv1y/qfg= github.com/trinsic-id/okapi/go v0.0.0-20211022163741-691fe8721335/go.mod h1:y2FOcdxdwphJl2D206kVPvKdskMm/Ro6WrnQv/nOCos= +github.com/trinsic-id/okapi/go v0.0.0-20211022175546-f12ecb2812cb h1:c/t8LnGU7DCI65WE3Vt5QHP3wXaGwp2ikQLa6fvkSdI= +github.com/trinsic-id/okapi/go v0.0.0-20211022175546-f12ecb2812cb/go.mod h1:y2FOcdxdwphJl2D206kVPvKdskMm/Ro6WrnQv/nOCos= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/go/services/services.go b/go/services/services.go index 6f7477842..0d3ad4d49 100644 --- a/go/services/services.go +++ b/go/services/services.go @@ -25,17 +25,20 @@ type ServiceBase struct { } type Service interface { - GetContext() (context.Context, error) + GetMetadataContext(userContext context.Context) (context.Context, error) GetMetadata() (metadata.MD, error) SetProfile(profile *sdk.WalletProfile) error } -func (s *ServiceBase) GetContext() (context.Context, error) { +func (s *ServiceBase) GetMetadataContext(userContext context.Context) (context.Context, error) { md, err := s.GetMetadata() if err != nil { return nil, err } - return metadata.NewOutgoingContext(context.Background(), md), nil + if userContext == nil { + return nil, errors.New("userContext cannot be nil") + } + return metadata.NewOutgoingContext(userContext, md), nil } func (s *ServiceBase) GetMetadata() (metadata.MD, error) { @@ -87,14 +90,14 @@ func (s *ServiceBase) SetProfile(profile *sdk.WalletProfile) error { type WalletService interface { Service - RegisterOrConnect(email string) error - CreateWallet(securityCode string) (*sdk.WalletProfile, error) - IssueCredential(document Document) (Document, error) - Search(query string) (*sdk.SearchResponse, error) - InsertItem(item Document) (string, error) - Send(document Document, email string) error - CreateProof(documentId string, revealDocument Document) (Document, error) - VerifyProof(proofDocument Document) (bool, error) + RegisterOrConnect(userContext context.Context, email string) error + CreateWallet(userContext context.Context, securityCode string) (*sdk.WalletProfile, error) + IssueCredential(userContext context.Context, document Document) (Document, error) + Search(userContext context.Context, query string) (*sdk.SearchResponse, error) + InsertItem(userContext context.Context, item Document) (string, error) + Send(userContext context.Context, document Document, email string) error + CreateProof(userContext context.Context, documentId string, revealDocument Document) (Document, error) + VerifyProof(userContext context.Context, proofDocument Document) (bool, error) } func CreateWalletService(serviceAddress string, channel *grpc.ClientConn) (WalletService, error) { @@ -150,12 +153,12 @@ type WalletBase struct { credentialClient sdk.CredentialClient } -func (w *WalletBase) RegisterOrConnect(email string) error { +func (w *WalletBase) RegisterOrConnect(userContext context.Context, email string) error { connectRequest := sdk.ConnectRequest{ ContactMethod: &sdk.ConnectRequest_Email{Email: email}, } - md, err := w.GetContext() + md, err := w.GetMetadataContext(userContext) if err != nil { return err } @@ -166,99 +169,40 @@ func (w *WalletBase) RegisterOrConnect(email string) error { return nil } -func (w *WalletBase) CreateWallet(securityCode string) (*sdk.WalletProfile, error) { - configuration, err := w.walletClient.GetProviderConfiguration(context.Background(), &sdk.GetProviderConfigurationRequest{}) - if err != nil { - return nil, err - } - +func (w *WalletBase) CreateWallet(userContext context.Context, securityCode string) (*sdk.WalletProfile, error) { dk := okapi.DidKey() - dc := okapi.DidComm() - - resolveResponse, err := dk.Resolve(&okapiproto.ResolveRequest{Did: configuration.KeyAgreementKeyId}) - if err != nil { - return nil, err - } - - var providerExchangeKey *okapiproto.JsonWebKey - for _, key := range resolveResponse.Keys { - if key.Kid == configuration.KeyAgreementKeyId { - providerExchangeKey = key - break - } - } + // Generate new DID used by the current device myKey, err := dk.Generate(&okapiproto.GenerateKeyRequest{KeyType: okapiproto.KeyType_KEY_TYPE_ED25519}) if err != nil { return nil, err } - var myExchangeKey *okapiproto.JsonWebKey - for _, key := range myKey.Key { - if key.Crv == "X25519" { - myExchangeKey = key - break - } - } - myDidDocument := myKey.DidDocument.AsMap() - walletRequest := sdk.CreateWalletRequest{ + walletRequest := &sdk.CreateWalletRequest{ Controller: myDidDocument["id"].(string), - Description: "My Cloud Wallet", SecurityCode: securityCode, } - walletBytes, err := proto.Marshal(&walletRequest) - if err != nil { - return nil, err - } - packedMessage, err := dc.Pack(&okapiproto.PackRequest{ - SenderKey: myExchangeKey, - ReceiverKey: providerExchangeKey, - Plaintext: walletBytes, - }) - if err != nil { - return nil, err - } - packedSdkMessage := packedMessage.Message - response, err := w.walletClient.CreateWalletEncrypted(context.Background(), packedSdkMessage) + createWalletResponse, err := w.walletClient.CreateWallet(userContext, walletRequest) if err != nil { return nil, err } - decryptedResponse, err := dc.Unpack(&okapiproto.UnpackRequest{ - SenderKey: providerExchangeKey, - ReceiverKey: myExchangeKey, - Message: response, - }) - if err != nil { - return nil, err - } - - createWalletResponse := sdk.CreateWalletResponse{} - err = proto.Unmarshal(decryptedResponse.Plaintext, &createWalletResponse) - if err != nil { - return nil, err - } - - myKeyJwk, err := proto.Marshal(myKey.Key[0]) - if err != nil { - return nil, err - } - jsonString, err := json.Marshal(myDidDocument) + keyBytes, err := proto.Marshal(myKey.Key[0]) if err != nil { return nil, err } return &sdk.WalletProfile{ - DidDocument: &sdk.JsonPayload{Json: &sdk.JsonPayload_JsonString{JsonString: string(jsonString)}}, + DidDocument: &sdk.JsonPayload{Json: &sdk.JsonPayload_JsonStruct{JsonStruct: myKey.DidDocument}}, WalletId: createWalletResponse.WalletId, Invoker: createWalletResponse.Invoker, Capability: createWalletResponse.Capability, - InvokerJwk: myKeyJwk, + InvokerJwk: keyBytes, }, nil } -func (w *WalletBase) IssueCredential(document Document) (Document, error) { +func (w *WalletBase) IssueCredential(userContext context.Context, document Document) (Document, error) { jsonBytes, err := json.Marshal(document) if err != nil { return nil, err @@ -271,7 +215,7 @@ func (w *WalletBase) IssueCredential(document Document) (Document, error) { }, } - md, err := w.GetContext() + md, err := w.GetMetadataContext(userContext) if err != nil { return nil, err } @@ -287,8 +231,8 @@ func (w *WalletBase) IssueCredential(document Document) (Document, error) { return doc, nil } -func (w *WalletBase) Search(query string) (*sdk.SearchResponse, error) { - md, err := w.GetContext() +func (w *WalletBase) Search(userContext context.Context, query string) (*sdk.SearchResponse, error) { + md, err := w.GetMetadataContext(userContext) if err != nil { return nil, err } @@ -301,12 +245,12 @@ func (w *WalletBase) Search(query string) (*sdk.SearchResponse, error) { return response, nil } -func (w *WalletBase) InsertItem(item Document) (string, error) { +func (w *WalletBase) InsertItem(userContext context.Context, item Document) (string, error) { jsonString, err := json.Marshal(item) if err != nil { return "", err } - md, err := w.GetContext() + md, err := w.GetMetadataContext(userContext) if err != nil { return "", err } @@ -323,12 +267,12 @@ func (w *WalletBase) InsertItem(item Document) (string, error) { return response.ItemId, nil } -func (w *WalletBase) Send(document Document, email string) error { +func (w *WalletBase) Send(userContext context.Context, document Document, email string) error { jsonString, err := json.Marshal(document) if err != nil { return err } - md, err := w.GetContext() + md, err := w.GetMetadataContext(userContext) if err != nil { return err } @@ -348,12 +292,12 @@ func (w *WalletBase) Send(document Document, email string) error { return nil } -func (w *WalletBase) CreateProof(documentId string, revealDocument Document) (Document, error) { +func (w *WalletBase) CreateProof(userContext context.Context, documentId string, revealDocument Document) (Document, error) { jsonString, err := json.Marshal(revealDocument) if err != nil { return nil, err } - md, err := w.GetContext() + md, err := w.GetMetadataContext(userContext) if err != nil { return nil, err } @@ -376,12 +320,12 @@ func (w *WalletBase) CreateProof(documentId string, revealDocument Document) (Do return proofMap, nil } -func (w *WalletBase) VerifyProof(proofDocument Document) (bool, error) { +func (w *WalletBase) VerifyProof(userContext context.Context, proofDocument Document) (bool, error) { jsonString, err := json.Marshal(proofDocument) if err != nil { return false, err } - md, err := w.GetContext() + md, err := w.GetMetadataContext(userContext) if err != nil { return false, err } @@ -400,8 +344,8 @@ func (w *WalletBase) VerifyProof(proofDocument Document) (bool, error) { type ProviderService interface { Service - InviteParticipant(request *sdk.InviteRequest) (*sdk.InviteResponse, error) - InvitationStatus(request *sdk.InvitationStatusRequest) (*sdk.InvitationStatusResponse, error) + InviteParticipant(userContext context.Context, request *sdk.InviteRequest) (*sdk.InviteResponse, error) + InvitationStatus(userContext context.Context, request *sdk.InvitationStatusRequest) (*sdk.InvitationStatusResponse, error) } type ProviderBase struct { @@ -424,21 +368,21 @@ func CreateProviderService(serviceAddress string, channel *grpc.ClientConn) (Pro return service, nil } -func (p *ProviderBase) InviteParticipant(request *sdk.InviteRequest) (*sdk.InviteResponse, error) { +func (p *ProviderBase) InviteParticipant(userContext context.Context, request *sdk.InviteRequest) (*sdk.InviteResponse, error) { // Verify contact method is set switch request.ContactMethod.(type) { case nil: return nil, fmt.Errorf("unset contact method") } - response, err := p.providerClient.Invite(context.Background(), request) + response, err := p.providerClient.Invite(userContext, request) if err != nil { return nil, err } return response, nil } -func (p *ProviderBase) InvitationStatus(request *sdk.InvitationStatusRequest) (*sdk.InvitationStatusResponse, error) { - response, err := p.providerClient.InvitationStatus(context.Background(), request) +func (p *ProviderBase) InvitationStatus(userContext context.Context, request *sdk.InvitationStatusRequest) (*sdk.InvitationStatusResponse, error) { + response, err := p.providerClient.InvitationStatus(userContext, request) if err != nil { return nil, err } diff --git a/go/services/services_test.go b/go/services/services_test.go index 85142f60a..601276562 100644 --- a/go/services/services_test.go +++ b/go/services/services_test.go @@ -1,6 +1,7 @@ package services import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -42,15 +43,19 @@ func TestServiceBase_SetProfile(t *testing.T) { if !assert.Nil(err) { return } - demoWallet, err := walletService.CreateWallet("") + demoWallet, err := walletService.CreateWallet(context.Background(), "") if !assert.Nil(err) { return } err = base.SetProfile(demoWallet) - assert.NoError(err) + if !assert.NoError(err) { + return + } ctxt, err = base.GetMetadata() - assert.NoError(err) + if !assert.NoError(err) { + return + } assert.NotNil(ctxt) } @@ -86,19 +91,19 @@ func TestVaccineCredentials(t *testing.T) { } // SETUP ACTORS // Create 3 different profiles for each participant in the scenario - allison, err := walletService.CreateWallet("") + allison, err := walletService.CreateWallet(context.Background(), "") failError(t, "error creating wallet", err) if !assert.NotNil(allison) { return } - clinic, err := walletService.CreateWallet("") + clinic, err := walletService.CreateWallet(context.Background(), "") failError(t, "error creating wallet", err) if !assert.NotNil(clinic) { return } - airline, err := walletService.CreateWallet("") + airline, err := walletService.CreateWallet(context.Background(), "") failError(t, "error creating wallet", err) if !assert.NotNil(airline) { return @@ -120,7 +125,7 @@ func TestVaccineCredentials(t *testing.T) { err = json.Unmarshal(fileContent, &credentialJson) failError(t, "error parsing JSON", err) - credential, err := walletService.IssueCredential(credentialJson) + credential, err := walletService.IssueCredential(context.Background(), credentialJson) failError(t, "error issuing credential", err) fmt.Printf("Credential:%s\n", credential) @@ -129,7 +134,7 @@ func TestVaccineCredentials(t *testing.T) { // Alice stores the credential in her cloud wallet. err = walletService.SetProfile(allison) failError(t, "error setting profile", err) - itemId, err := walletService.InsertItem(credential) + itemId, err := walletService.InsertItem(context.Background(), credential) failError(t, "error inserting item", err) fmt.Println("item id", itemId) @@ -146,7 +151,7 @@ func TestVaccineCredentials(t *testing.T) { err = json.Unmarshal(fileContent2, &proofRequestJson) failError(t, "error parsing JSON", err) - credentialProof, err := walletService.CreateProof(itemId, proofRequestJson) + credentialProof, err := walletService.CreateProof(context.Background(), itemId, proofRequestJson) failError(t, "error creating proof", err) fmt.Println("Credential proof", credentialProof) @@ -154,7 +159,7 @@ func TestVaccineCredentials(t *testing.T) { // The airline verifies the credential err = walletService.SetProfile(airline) failError(t, "error setting profile", err) - valid, err := walletService.VerifyProof(credentialProof) + valid, err := walletService.VerifyProof(context.Background(), credentialProof) failError(t, "error verifying proof", err) fmt.Println("Validation result", valid) if valid != true { @@ -192,7 +197,7 @@ func TestProviderService_InviteParticipant(t *testing.T) { fmt.Printf("%+v\n", walletService) - wallet, err := walletService.CreateWallet("") + wallet, err := walletService.CreateWallet(context.Background(), "") if !assert.Nil(err) || !assert.NotNil(wallet) { return } @@ -205,7 +210,7 @@ func TestProviderService_InviteParticipant(t *testing.T) { // The issue was not throwing an error that the profile isn't set, but we don't need a wallet profile, so use a // context without metadata attached. See method definition. - inviteResponse, err := providerService.InviteParticipant(&sdk.InviteRequest{ + inviteResponse, err := providerService.InviteParticipant(context.Background(), &sdk.InviteRequest{ Participant: sdk.ParticipantType_participant_type_individual, Description: "I dunno", ContactMethod: &sdk.InviteRequest_Email{ @@ -216,16 +221,6 @@ func TestProviderService_InviteParticipant(t *testing.T) { panic(err) } fmt.Printf("%+v\n", inviteResponse) - - // TODO - Verify invitation status - //inviteStatus, err := providerService.InvitationStatus(&sdk.InvitationStatusRequest{ - // InvitationId: inviteResponse.InvitationId, - //}) - //if err != nil { - // panic(err) - //} - //fmt.Printf("%+v\n", inviteStatus) - //assert.Equal(t, sdk.InvitationStatusResponse_InvitationSent, inviteStatus.Status) } func failError(t *testing.T, message string, err error) { diff --git a/python/trinsic/services.py b/python/trinsic/services.py index 8eb6ad840..1dc8c33af 100644 --- a/python/trinsic/services.py +++ b/python/trinsic/services.py @@ -13,8 +13,7 @@ from trinsic.proto.services.common.v1 import JsonPayload, RequestOptions, JsonFormat from trinsic.proto.services.provider.v1 import ProviderStub, InviteRequestDidCommInvitation, InviteResponse, \ ParticipantType, InvitationStatusResponse -from trinsic.proto.services.trustregistry.v1 import TrustRegistryStub, AddFrameworkRequest, GovernanceFramework, \ - RegistrationStatus +from trinsic.proto.services.trustregistry.v1 import TrustRegistryStub, GovernanceFramework, RegistrationStatus from trinsic.proto.services.universalwallet.v1 import WalletProfile, WalletStub, SearchResponse from trinsic.proto.services.verifiablecredentials.v1 import CredentialStub @@ -154,7 +153,7 @@ def __del__(self): async def register_governance_framework(self, governance_framework: str, description: str): governance_url = urllib.parse.urlsplit(governance_framework, allow_fragments=False) - # TODO - Verify complete url + # Verify complete url if governance_url.scheme and governance_url.netloc and governance_url.path: self.provider_client.metadata = self.metadata response = await self.provider_client.add_framework(governance_framework=GovernanceFramework(