Skip to content

Commit

Permalink
feat: Create CLI to create config files
Browse files Browse the repository at this point in the history
closes trustbloc#139

Signed-off-by: Firas Qutishat <[email protected]>
  • Loading branch information
fqutishat committed May 24, 2020
1 parent ceba77d commit 3c660c4
Show file tree
Hide file tree
Showing 23 changed files with 840 additions and 123 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,4 @@ coverage.txt
.build
*.log
test/bdd/fixtures/keys/tls
test/bdd/fixtures/discovery-server/config
test/bdd/fixtures/stakeholder-server/config
test/bdd/fixtures/wellknown/jws
15 changes: 9 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ unit-test:
@scripts/check_unit.sh

.PHONY: bdd-test
bdd-test: generate-test-config did-method-rest-docker generate-test-keys
bdd-test: clean did-method-cli did-method-rest-docker generate-test-keys
@scripts/check_integration.sh

.PHONY: did-method-rest
Expand All @@ -39,6 +39,13 @@ did-method-rest:
@mkdir -p ./.build/bin
@cd cmd/did-method-rest && go build -o ../../.build/bin/did-method main.go

.PHONY: did-method-cli
did-method-cli:
@echo "Building did-method-cli"
@mkdir -p ./.build/bin
@mkdir -p ./test/bdd/fixtures/wellknown/jws
@cd cmd/did-method-cli && go build -o ../../.build/bin/cli main.go


.PHONY: did-method-rest-docker
did-method-rest-docker:
Expand All @@ -47,12 +54,8 @@ did-method-rest-docker:
--build-arg GO_VER=$(GO_VER) \
--build-arg ALPINE_VER=$(ALPINE_VER) .

.PHONY: generate-test-config
generate-test-config:
@scripts/generate_test_config.sh

.PHONY: generate-test-keys
generate-test-keys: clean
generate-test-keys:
@mkdir -p -p test/bdd/fixtures/keys/tls
@docker run -i --rm \
-v $(abspath .):/opt/workspace/trustbloc-did-method \
Expand Down
314 changes: 314 additions & 0 deletions cmd/did-method-cli/createconfigcmd/createconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package createconfigcmd

import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"

docdid "github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/jose"
"github.com/spf13/cobra"
cmdutils "github.com/trustbloc/edge-core/pkg/utils/cmd"
tlsutils "github.com/trustbloc/edge-core/pkg/utils/tls"

"github.com/trustbloc/trustbloc-did-method/pkg/did"
"github.com/trustbloc/trustbloc-did-method/pkg/vdri/trustbloc/models"
)

const (
sidetreeURLFlagName = "sidetree-url"
sidetreeURLFlagUsage = "Sidetree url." +
" Alternatively, this can be set with the following environment variable: " + sidetreeURLEnvKey
sidetreeURLEnvKey = "DID_METHOD_CLI_SIDETREE_URL"

tlsSystemCertPoolFlagName = "tls-systemcertpool"
tlsSystemCertPoolFlagUsage = "Use system certificate pool." +
" Possible values [true] [false]. Defaults to false if not set." +
" Alternatively, this can be set with the following environment variable: " + tlsSystemCertPoolEnvKey
tlsSystemCertPoolEnvKey = "DID_METHOD_CLI_TLS_SYSTEMCERTPOOL"

tlsCACertsFlagName = "tls-cacerts"
tlsCACertsFlagUsage = "Comma-Separated list of ca certs path." +
" Alternatively, this can be set with the following environment variable: " + tlsCACertsEnvKey
tlsCACertsEnvKey = "DID_METHOD_CLI_TLS_CACERTS"

sidetreeWriteTokenFlagName = "sidetree-write-token"
sidetreeWriteTokenEnvKey = "DID_METHOD_CLI_SIDETREE_WRITE_TOKEN" //nolint: gosec
sidetreeWriteTokenFlagUsage = "The sidetree write token " +
" Alternatively, this can be set with the following environment variable: " + sidetreeWriteTokenEnvKey

configFileFlagName = "config-file"
configFileEnvKey = "DID_METHOD_CLI_CONFIG_FILE"
configFileFlagUsage = "Config file include data required for creating well known config files " +
" Alternatively, this can be set with the following environment variable: " + configFileEnvKey

outputDirectoryFlagName = "output-directory"
outputDirectoryEnvKey = "DID_METHOD_CLI_OUTPUT_DIRECTORY" //nolint: gosec
outputDirectoryFlagUsage = "Output directory " +
" Alternatively, this can be set with the following environment variable: " + outputDirectoryEnvKey
)

type config struct {
ConsortiumData consortiumData `json:"consortium_data,omitempty"`
MembersData []*memberData `json:"members_data,omitempty"`
}

type consortiumData struct {
// Domain is the domain name of the consortium
Domain string `json:"domain,omitempty"`
// Policy contains the consortium policy configuration
Policy models.ConsortiumPolicy `json:"policy"`
}

type memberData struct {
// Domain is the domain name of the member
Domain string `json:"domain,omitempty"`
// Policy contains stakeholder-specific configuration settings
Policy models.StakeholderSettings `json:"policy"`
// Endpoints is a list of sidetree endpoints owned by this stakeholder organization
Endpoints []string `json:"endpoints"`
// PrivateKeyJwk is privatekey jwk
PrivateKeyJwk json.RawMessage `json:"privateKeyJwk,omitempty"`

jsonWebKey jose.JWK
}

type didClient interface {
CreateDID(domain string, opts ...did.CreateDIDOption) (*docdid.Doc, error)
}

type parameters struct {
sidetreeURL string
didClient didClient
config *config
}

// GetCreateConfigCmd returns the Cobra create conifg command.
func GetCreateConfigCmd() *cobra.Command {
createConfigCmd := createCreateConfigCmd()

createFlags(createConfigCmd)

return createConfigCmd
}

func createCreateConfigCmd() *cobra.Command {
return &cobra.Command{
Use: "create-config",
Short: "Create did method config file",
Long: "Create did method config file",
RunE: func(cmd *cobra.Command, args []string) error {
sidetreeURL, err := cmdutils.GetUserSetVarFromString(cmd, sidetreeURLFlagName, sidetreeURLEnvKey,
false)
if err != nil {
return err
}

rootCAs, err := getRootCAs(cmd)
if err != nil {
return err
}

sidetreeWriteToken, err := cmdutils.GetUserSetVarFromString(cmd, sidetreeWriteTokenFlagName,
sidetreeWriteTokenEnvKey, true)
if err != nil {
return err
}

outputDirectory, err := cmdutils.GetUserSetVarFromString(cmd, outputDirectoryFlagName,
outputDirectoryEnvKey, true)
if err != nil {
return err
}

config, err := getConfig(cmd)
if err != nil {
return err
}

parameters := &parameters{
sidetreeURL: strings.TrimSpace(sidetreeURL),
didClient: did.New(did.WithAuthToken(sidetreeWriteToken),
did.WithTLSConfig(&tls.Config{RootCAs: rootCAs})),
config: config,
}

filesData, err := createConfig(parameters)
if err != nil {
return err
}

return writeConfig(outputDirectory, filesData)
},
}
}

func writeConfig(outputDirectory string, filesData map[string][]byte) error {
for k, v := range filesData {
if err := os.MkdirAll(outputDirectory, os.ModePerm); err != nil {
return err
}

f, err := os.Create(path.Join(outputDirectory, k+".json"))
if err != nil {
return err
}

_, err = f.Write(v)
if err != nil {
return err
}
}

return nil
}

func getConfig(cmd *cobra.Command) (*config, error) {
configFile, err := cmdutils.GetUserSetVarFromString(cmd, configFileFlagName,
configFileEnvKey, false)
if err != nil {
return nil, err
}

data, err := ioutil.ReadFile(configFile) //nolint: gosec
if err != nil {
return nil, fmt.Errorf("failed to read input descriptors file '%s' : %w", configFile, err)
}

var config config

if err := json.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed unmarshal to config %w", err)
}

for _, member := range config.MembersData {
if err := member.jsonWebKey.UnmarshalJSON(member.PrivateKeyJwk); err != nil {
return nil, err
}
}

return &config, nil
}

func getRootCAs(cmd *cobra.Command) (*x509.CertPool, error) {
tlsSystemCertPoolString, err := cmdutils.GetUserSetVarFromString(cmd, tlsSystemCertPoolFlagName,
tlsSystemCertPoolEnvKey, true)
if err != nil {
return nil, err
}

tlsSystemCertPool := false
if tlsSystemCertPoolString != "" {
tlsSystemCertPool, err = strconv.ParseBool(tlsSystemCertPoolString)
if err != nil {
return nil, err
}
}

tlsCACerts, err := cmdutils.GetUserSetVarFromArrayString(cmd, tlsCACertsFlagName,
tlsCACertsEnvKey, true)
if err != nil {
return nil, err
}

return tlsutils.GetCertPool(tlsSystemCertPool, tlsCACerts)
}

func createFlags(startCmd *cobra.Command) {
startCmd.Flags().StringP(sidetreeURLFlagName, "", "", sidetreeURLFlagUsage)
startCmd.Flags().StringP(tlsSystemCertPoolFlagName, "", "",
tlsSystemCertPoolFlagUsage)
startCmd.Flags().StringArrayP(tlsCACertsFlagName, "", []string{}, tlsCACertsFlagUsage)
startCmd.Flags().StringP(sidetreeWriteTokenFlagName, "", "", sidetreeWriteTokenFlagUsage)
startCmd.Flags().StringP(configFileFlagName, "", "", configFileFlagUsage)
startCmd.Flags().StringP(outputDirectoryFlagName, "", "", outputDirectoryFlagUsage)
}

func createConfig(parameters *parameters) (map[string][]byte, error) {
filesData := make(map[string][]byte)

consortium := models.Consortium{Domain: parameters.config.ConsortiumData.Domain,
Policy: parameters.config.ConsortiumData.Policy}

for _, member := range parameters.config.MembersData {
didDoc, err := createDID(parameters.didClient, parameters.sidetreeURL, &member.jsonWebKey)
if err != nil {
return nil, err
}

pubKey, err := member.jsonWebKey.Public().MarshalJSON()
if err != nil {
return nil, err
}

consortium.Members = append(consortium.Members, &models.StakeholderListElement{Domain: member.Domain,
DID: didDoc.ID, PublicKey: models.PublicKey{ID: didDoc.ID + "#" + member.jsonWebKey.KeyID,
JWK: pubKey}})

stakeholder := models.Stakeholder{Domain: member.Domain, DID: didDoc.ID,
Policy: member.Policy, Endpoints: member.Endpoints}

stakeholderBytes, err := json.Marshal(stakeholder)
if err != nil {
return nil, err
}

jwsBytes, err := signConfig(stakeholderBytes)
if err != nil {
return nil, err
}

filesData[member.Domain] = jwsBytes
}

consortiumBytes, err := json.Marshal(consortium)
if err != nil {
return nil, err
}

jwsBytes, err := signConfig(consortiumBytes)
if err != nil {
return nil, err
}

filesData[consortium.Domain] = jwsBytes

return filesData, nil
}

func signConfig(configBytes []byte) ([]byte, error) {
// TODO add logic for jws
// for now return dummy jws
// remove this code after adding logic for jws
m := make(map[string]interface{})
m["payload"] = base64.RawURLEncoding.EncodeToString(configBytes)

return json.Marshal(m)
}

func createDID(didClient didClient, sidetreeURL string, jwk *jose.JWK) (*docdid.Doc, error) {
pubKey, err := jwk.Public().MarshalJSON()
if err != nil {
return nil, err
}

return didClient.CreateDID("", did.WithSidetreeEndpoint(sidetreeURL), did.WithPublicKey(&did.PublicKey{
Type: did.Ed25519VerificationKey2018, Encoding: did.PublicKeyEncodingJwk, Value: pubKey, Recovery: true}),
did.WithPublicKey(&did.PublicKey{ID: jwk.KeyID,
Type: did.JWSVerificationKey2020, Encoding: did.PublicKeyEncodingJwk, KeyType: did.Ed25519KeyType,
Value: pubKey,
Usage: []string{did.KeyUsageGeneral}}))

}
16 changes: 16 additions & 0 deletions cmd/did-method-cli/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright SecureKey Technologies Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0

module github.com/trustbloc/trustbloc-did-method/cmd/did-method-cli

replace github.com/trustbloc/trustbloc-did-method => ../..

require (
github.com/hyperledger/aries-framework-go v0.1.4-0.20200521101441-dcc599e23d09
github.com/spf13/cobra v1.0.0
github.com/trustbloc/edge-core v0.1.3
github.com/trustbloc/trustbloc-did-method v0.0.0-00010101000000-000000000000
)

go 1.13
Loading

0 comments on commit 3c660c4

Please sign in to comment.