From b6feac7c71ced7357f43ad161146de9415f790fc Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Tue, 19 Nov 2024 17:34:58 -0800 Subject: [PATCH] provide useful utility to fetch domain attrs Signed-off-by: Henry Avetisyan --- .gitignore | 3 + assembly/utils/utils.xml | 4 + clients/go/zms/model.go | 7 + clients/go/zms/zms_schema.go | 1 + pom.xml | 2 + utils/zms-domainattrs/Makefile | 58 +++++++ utils/zms-domainattrs/README.md | 58 +++++++ utils/zms-domainattrs/doc.go | 7 + utils/zms-domainattrs/pom.xml | 73 +++++++++ utils/zms-domainattrs/zms-domainattrs.go | 194 +++++++++++++++++++++++ 10 files changed, 407 insertions(+) create mode 100644 utils/zms-domainattrs/Makefile create mode 100644 utils/zms-domainattrs/README.md create mode 100644 utils/zms-domainattrs/doc.go create mode 100644 utils/zms-domainattrs/pom.xml create mode 100644 utils/zms-domainattrs/zms-domainattrs.go diff --git a/.gitignore b/.gitignore index ce3cda5189e..ad196ec7ea4 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,9 @@ utils/zts-svccert/src/ utils/zts-accesstoken/bin/ utils/zts-accesstoken/pkg/ utils/zts-accesstoken/src/ +utils/zms-domainattrs/bin/ +utils/zms-domainattrs/pkg/ +utils/zms-domainattrs/src/ utils/zts-idtoken/bin/ utils/zts-idtoken/pkg/ utils/zts-idtoken/src/ diff --git a/assembly/utils/utils.xml b/assembly/utils/utils.xml index 6baaadfd3f7..43bf56a5261 100644 --- a/assembly/utils/utils.xml +++ b/assembly/utils/utils.xml @@ -34,6 +34,10 @@ ${basedir}/../../utils/zms-svctoken/target bin + + ${basedir}/../../utils/zms-domainattrs/target + bin + ${basedir}/../../utils/zts-roletoken/target bin diff --git a/clients/go/zms/model.go b/clients/go/zms/model.go index b3dad5b5d21..2ba06e29682 100644 --- a/clients/go/zms/model.go +++ b/clients/go/zms/model.go @@ -3429,6 +3429,13 @@ type ServiceIdentities struct { // list of services // List []*ServiceIdentity `json:"list"` + + // + // if set, the value indicates the total number of services in the system that + // match the query criteria but not returned due to limit constraints; thus, the + // result in the list is a partial set. + // + ServiceMatchCount int64 `json:"serviceMatchCount"` } // NewServiceIdentities - creates an initialized ServiceIdentities instance, returns a pointer to it diff --git a/clients/go/zms/zms_schema.go b/clients/go/zms/zms_schema.go index befb10284de..6d3bfce5979 100644 --- a/clients/go/zms/zms_schema.go +++ b/clients/go/zms/zms_schema.go @@ -446,6 +446,7 @@ func init() { tServiceIdentities := rdl.NewStructTypeBuilder("Struct", "ServiceIdentities") tServiceIdentities.Comment("The representation of list of services") tServiceIdentities.ArrayField("list", "ServiceIdentity", false, "list of services") + tServiceIdentities.Field("serviceMatchCount", "Int64", false, nil, "if set, the value indicates the total number of services in the system that match the query criteria but not returned due to limit constraints; thus, the result in the list is a partial set.") sb.AddType(tServiceIdentities.Build()) tServiceIdentityList := rdl.NewStructTypeBuilder("Struct", "ServiceIdentityList") diff --git a/pom.xml b/pom.xml index 6c9b3355508..29f6eca4b4a 100644 --- a/pom.xml +++ b/pom.xml @@ -203,6 +203,7 @@ provider/harness/sia-harness utils/zms-cli utils/athenz-conf + utils/zms-domainattrs utils/zms-svctoken utils/zpe-updater utils/zts-roletoken @@ -499,6 +500,7 @@ libs/go/athenzconf utils/zms-cli utils/athenz-conf + utils/zms-domainattrs utils/zms-svctoken utils/zpe-updater utils/zts-roletoken diff --git a/utils/zms-domainattrs/Makefile b/utils/zms-domainattrs/Makefile new file mode 100644 index 00000000000..5682ff9262f --- /dev/null +++ b/utils/zms-domainattrs/Makefile @@ -0,0 +1,58 @@ +# +# Makefile to build ZMS Domain Attributes utility +# Prerequisite: Go development environment +# +# Copyright The Athenz Authors +# Licensed under the Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 +# + +GOPKGNAME = github.com/AthenZ/athenz/utils/zms-domainattrs +PKG_DATE=$(shell date '+%Y-%m-%dT%H:%M:%S') +BINARY=zms-domainattrs +SRC=zms-domainattrs.go + +# check to see if go utility is installed +GO := $(shell command -v go 2> /dev/null) +GOPATH := $(shell pwd) +export $(GOPATH) + +ifdef GO + +# we need to make sure we have go 1.19+ +# the output for the go version command is: +# go version go1.19 darwin/amd64 + +GO_VER_GTEQ := $(shell expr `go version | cut -f 3 -d' ' | cut -f2 -d.` \>= 19) +ifneq "$(GO_VER_GTEQ)" "1" +all: + @echo "Please install 1.19.x or newer version of golang" +else + +.PHONY: vet fmt linux darwin +all: vet fmt linux darwin + +endif + +else + +all: + @echo "go is not available please install golang" + +endif + +vet: + go vet . + +fmt: + go fmt . + +darwin: + @echo "Building darwin client..." + GOOS=darwin go build -ldflags "-X main.VERSION=$(PKG_VERSION) -X main.BUILD_DATE=$(PKG_DATE)" -o target/darwin/$(BINARY) $(SRC) + +linux: + @echo "Building linux client..." + GOOS=linux go build -ldflags "-X main.VERSION=$(PKG_VERSION) -X main.BUILD_DATE=$(PKG_DATE)" -o target/linux/$(BINARY) $(SRC) + +clean: + rm -rf target diff --git a/utils/zms-domainattrs/README.md b/utils/zms-domainattrs/README.md new file mode 100644 index 00000000000..e5779153e7b --- /dev/null +++ b/utils/zms-domainattrs/README.md @@ -0,0 +1,58 @@ +zms-domainattrs +=============== + +The utility looks at the domains specified in a given file (one domain per line) and +for each domain, it retrieves and displays the requested attributes associated with +the domain. + +The utility supports the following list of attributes: + +- businessService +- productId +- account +- gcpProject +- gcpProjectNumber +- azureSubscription +- azureTenant +- azureClient +- org +- slackChannel +- environment + +For businessService and productId attributes, if the given domain does not have the +attribute set, the utility will look at the parent domain to see if the attribute +is set there. If the attribute is set in the parent domain, the utility will display +the value from the parent domain. It will continue to look at the parent domains until +it finds the attribute set, or it reaches the top level domain. + +## Usage + +``` +zms-domainattrs -svc-key-file ./key.pem -svc-cert-file ./cert.pem -zms https://athenz.io:4443/zms/v1 -domain-file ./domain.txt -attrs businessService,account +``` + +where domain.txt might contain: + +``` +weather +sports.prod +sports.nhl +sys.auth +``` + +And the output might look like ('weather' domain does not have a businessService attribute and +'sports.prod' domain does not have an account attribute): + +``` +Domain,businessService,account +weather,,123456 +sports.prod,sports-service, +sports.nhl,sports-service,123456 +sys.auth,athenz,456789 +``` + +## License + +Copyright The Athenz Authors + +Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) diff --git a/utils/zms-domainattrs/doc.go b/utils/zms-domainattrs/doc.go new file mode 100644 index 00000000000..3f6be9fd429 --- /dev/null +++ b/utils/zms-domainattrs/doc.go @@ -0,0 +1,7 @@ +// Copyright The Athenz Authors +// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms. + +// The utility looks at the domains specified in a given file (one domain per line) and +// for each domain, it retrieves and displays the requested attributes associated with +// the domain. +package main diff --git a/utils/zms-domainattrs/pom.xml b/utils/zms-domainattrs/pom.xml new file mode 100644 index 00000000000..31294319ec4 --- /dev/null +++ b/utils/zms-domainattrs/pom.xml @@ -0,0 +1,73 @@ + + + + 4.0.0 + + + com.yahoo.athenz + athenz + 1.12.5-SNAPSHOT + ../../pom.xml + + + zms-domainattrs + jar + zms-domainattrs + ZMS Domain Attribute Lookup Utility + + + true + true + + + + + + org.codehaus.mojo + exec-maven-plugin + ${maven-exec-plugin.version} + + + + exec + + compile + + + + make + + PKG_VERSION=${project.parent.version} + clean + all + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + default-jar + + + + + + + + diff --git a/utils/zms-domainattrs/zms-domainattrs.go b/utils/zms-domainattrs/zms-domainattrs.go new file mode 100644 index 00000000000..05387c24d44 --- /dev/null +++ b/utils/zms-domainattrs/zms-domainattrs.go @@ -0,0 +1,194 @@ +// Copyright The Athenz Authors +// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms. + +package main + +import ( + "flag" + "fmt" + "log" + "os" + "strings" + + "github.com/AthenZ/athenz/clients/go/zms" + "github.com/AthenZ/athenz/libs/go/athenzutils" +) + +var ( + // VERSION gets set by the build script via the LDFLAGS. + VERSION string + + // BUILD_DATE gets set by the build script via the LDFLAGS. + BUILD_DATE string +) + +func usage() { + fmt.Println("usage: zms-domainattrs -svc-key-file -svc-cert-file -zms -domain-file [-attrs ]") + os.Exit(1) +} + +func printVersion() { + if VERSION == "" { + fmt.Println("zms-domainattrs (development version)") + } else { + fmt.Println("zms-domainattrs " + VERSION + " " + BUILD_DATE) + } +} + +func main() { + var domainFile, attrs, svcKeyFile, svcCertFile, svcCACertFile, zmsURL string + var showVersion bool + flag.StringVar(&domainFile, "domain-file", "", "domain file with list of domains") + flag.StringVar(&svcCACertFile, "svc-cacert-file", "", "CA Certificates file") + flag.StringVar(&svcKeyFile, "svc-key-file", "", "service identity private key file") + flag.StringVar(&svcCertFile, "svc-cert-file", "", "service identity certificate file") + flag.StringVar(&zmsURL, "zms", "", "url of the ZMS Service") + flag.StringVar(&attrs, "attrs", "businessService,productId,account,gcpProject,gcpProjectNumber,azureSubscription,azureTenant,azureClient,org,slackChannel,environment", "comma separated list of domain attribute names") + flag.BoolVar(&showVersion, "version", false, "Show version") + flag.Parse() + + if showVersion { + printVersion() + return + } + + if domainFile == "" || svcKeyFile == "" || svcCertFile == "" || zmsURL == "" { + usage() + } + + // first get the list of domains from the file + + domains, err := getDomainList(domainFile) + if err != nil { + log.Fatalf("unable to get domain list from file: %s error: %v\n", domainFile, err) + } + + fetchDomainAttrs(zmsURL, svcKeyFile, svcCertFile, svcCACertFile, domains, attrs) +} + +func getDomainList(domainFile string) ([]string, error) { + + bytes, err := os.ReadFile(domainFile) + if err != nil { + return nil, err + } + + return strings.Split(string(bytes), "\n"), nil +} + +func fetchDomainAttrs(zmsURL, svcKeyFile, svcCertFile, svcCACertFile string, domains []string, attrs string) { + + client, err := athenzutils.ZmsClient(zmsURL, svcKeyFile, svcCertFile, svcCACertFile, false) + if err != nil { + log.Fatalf("unable to create zms client: %v\n", err) + } + + signedDomains, _, err := client.GetSignedDomains("", "true", "all", nil, nil, "") + if err != nil { + log.Fatalf("unable to fetch domains with list of attributes from ZMS: %v\n", err) + } + + // put the results in a map + + domainMap := make(map[string]*zms.SignedDomain, len(signedDomains.Domains)) + for _, signedDomain := range signedDomains.Domains { + domainMap[string(signedDomain.Domain.Name)] = signedDomain + } + + // convert the attributes to list + + attrList := strings.Split(attrs, ",") + + // write the header line + + fmt.Print("Domain") + for _, attr := range attrList { + fmt.Print("," + attr) + } + fmt.Println() + + // go through the list of domains and print the requested attributes + + for _, domain := range domains { + + if domain == "" { + continue + } + + fmt.Print(domain) + signedDomain, ok := domainMap[domain] + if !ok { + fmt.Println(",") + continue + } + + // now go through the list of attributes and print the values + + for _, attr := range attrList { + attrName := strings.ToLower(attr) + attrVal := getDomainAttributeValue(signedDomain.Domain, attrName) + if attrVal == "" && isRecursiveAttribute(attrName) { + attrVal = getDomainAttributeValueRecursive(domain, domainMap, attrName) + } + fmt.Print("," + attrVal) + } + fmt.Println() + } + fmt.Println() +} + +func getParentDomainName(domainName string) string { + idx := strings.LastIndex(domainName, ".") + if idx == -1 { + return "" + } + return domainName[:idx] +} + +func getDomainAttributeValueRecursive(domainName string, domainMap map[string]*zms.SignedDomain, attrName string) string { + // first get the parent domain name + parentDomainName := getParentDomainName(domainName) + if parentDomainName == "" { + return "" + } + signedDomain, ok := domainMap[parentDomainName] + if !ok { + return "" + } + attrValue := getDomainAttributeValue(signedDomain.Domain, attrName) + if attrValue == "" { + attrValue = getDomainAttributeValueRecursive(parentDomainName, domainMap, attrName) + } + return attrValue +} + +func isRecursiveAttribute(attrName string) bool { + return attrName == "businessservice" || attrName == "productid" +} + +func getDomainAttributeValue(domainData *zms.DomainData, attrName string) string { + switch attrName { + case "businessservice": + return domainData.BusinessService + case "productid": + return domainData.ProductId + case "account": + return domainData.Account + case "gcpproject": + return domainData.GcpProject + case "gcpprojectnumber": + return domainData.GcpProjectNumber + case "azuresubscription": + return domainData.AzureSubscription + case "azuretenant": + return domainData.AzureTenant + case "azureclient": + return domainData.AzureClient + case "org": + return string(domainData.Org) + case "slackchannel": + return domainData.SlackChannel + } + + return "" +}