Skip to content

Commit

Permalink
Issue #3979 - Feature Request: Modify LIST org secrets API to return …
Browse files Browse the repository at this point in the history
…node/user data

Signed-off-by: Max McAdam <[email protected]>
  • Loading branch information
MaxMcAdam committed Feb 19, 2024
1 parent 70bc7d6 commit bd78a8d
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 13 deletions.
2 changes: 2 additions & 0 deletions agreementbot/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type AgbotSecrets interface {
IsReady() bool
GetLastVaultStatus() uint64

ListAllSecrets(user, token, org, path string) ([]string, error)

ListOrgSecret(user, token, org, path string) error
ListOrgSecrets(user, token, org, path string) ([]string, error)
CreateOrgSecret(user, token, org, path string, data SecretDetails) error
Expand Down
22 changes: 16 additions & 6 deletions agreementbot/secrets/vault/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,24 @@ func (vs *AgbotVaultSecrets) listSecret(user, token, org, name, url string) erro
return nil
}

// List all secrets in the specified org in vault.
func (vs *AgbotVaultSecrets) ListAllSecrets(user, token, org, path string) ([]string, error) {

glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("list all secrets in %v", org)))

url := fmt.Sprintf("%s/v1/openhorizon/metadata/%s"+cliutils.AddSlash(path), vs.cfg.GetAgbotVaultURL(), org)
glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("listing all secrets in org %s", org)))
return vs.listSecrets(user, token, org, url, path, true)
}

// List all org-level secrets at a specified path in vault.
func (vs *AgbotVaultSecrets) ListOrgSecrets(user, token, org, path string) ([]string, error) {

glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("list secrets in %v", org)))

url := fmt.Sprintf("%s/v1/openhorizon/metadata/%s"+cliutils.AddSlash(path), vs.cfg.GetAgbotVaultURL(), org)
glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("listing secrets in org %s", org)))
return vs.listSecrets(user, token, org, url, path)
return vs.listSecrets(user, token, org, url, path, false)
}

// List all user-level secrets at a specified path in vault.
Expand All @@ -135,7 +145,7 @@ func (vs *AgbotVaultSecrets) ListOrgUserSecrets(user, token, org, path string) (
glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("listing secrets for user %v in %v", user, org)))

url := fmt.Sprintf("%s/v1/openhorizon/metadata/%s"+cliutils.AddSlash(path), vs.cfg.GetAgbotVaultURL(), org)
secrets, err := vs.listSecrets(user, token, org, url, path)
secrets, err := vs.listSecrets(user, token, org, url, path, false)
if err != nil {
return nil, err
}
Expand All @@ -154,7 +164,7 @@ func (vs *AgbotVaultSecrets) ListOrgNodeSecrets(user, token, org, node, path str
glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("listing secrets for node %v in %v", node, org)))

url := fmt.Sprintf("%s/v1/openhorizon/metadata/%s"+cliutils.AddSlash(path), vs.cfg.GetAgbotVaultURL(), org)
secrets, err := vs.listSecrets(user, token, org, url, path)
secrets, err := vs.listSecrets(user, token, org, url, path, false)
if err != nil {
return nil, err
}
Expand All @@ -173,7 +183,7 @@ func (vs *AgbotVaultSecrets) ListUserNodeSecrets(user, token, org, node, path st
glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("listing secrets for node %v in %v as user %v", node, org, user)))

url := fmt.Sprintf("%s/v1/openhorizon/metadata/%s"+cliutils.AddSlash(path), vs.cfg.GetAgbotVaultURL(), org)
secrets, err := vs.listSecrets(user, token, org, url, path)
secrets, err := vs.listSecrets(user, token, org, url, path, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -212,7 +222,7 @@ func (vs *AgbotVaultSecrets) gatherSecretNames(user, token, org, path string, qu
}

// List the secrets at a specified path within the vault
func (vs *AgbotVaultSecrets) listSecrets(user, token, org, url, path string) ([]string, error) {
func (vs *AgbotVaultSecrets) listSecrets(user, token, org, url, path string, allSecrets bool) ([]string, error) {

glog.V(3).Infof(vaultPluginLogString(fmt.Sprintf("url: %s", url)))

Expand Down Expand Up @@ -272,7 +282,7 @@ func (vs *AgbotVaultSecrets) listSecrets(user, token, org, url, path string) ([]
secrets := make([]string, 0)
if path == "" {
for _, secret := range respMsg.Data.Keys {
if secret != "user/" && secret != "node/" {
if allSecrets || (secret != "user/" && secret != "node/") {
secrets = append(secrets, secret)
}
}
Expand Down
62 changes: 59 additions & 3 deletions agreementbot/secure_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func (a *SecureAPI) listen() {
router.HandleFunc(`/org/{org}/secrets/user/{user}/{secret:[\w\/\-]+}`, a.userSecret).Methods("GET", "LIST", "PUT", "POST", "DELETE", "OPTIONS")
router.HandleFunc(`/org/{org}/secrets/node/{node}/{secret:[\w\/\-]+}`, a.nodeSecret).Methods("GET", "LIST", "PUT", "POST", "DELETE", "OPTIONS")
router.HandleFunc("/org/{org}/secrets", a.orgSecrets).Methods("LIST", "OPTIONS")
router.HandleFunc("/org/{org}/allsecrets", a.allSecrets).Methods("LIST", "OPTIONS")
router.HandleFunc(`/org/{org}/secrets/{secret:[\w\/\-]+}`, a.orgSecret).Methods("GET", "LIST", "PUT", "POST", "DELETE", "OPTIONS")
router.HandleFunc("/org/{org}/hagroup/{group}/nodemanagement/{node}/{nmpid}", a.haNodeNMPUpdateRequest).Methods("POST", "OPTIONS")

Expand Down Expand Up @@ -1188,6 +1189,7 @@ type SecretRequestInfo struct {
user string // if applicable, the user whose resources are being accessed
node string // if applicable, the node whose resources are being accessed
vaultSecretName string // the name of the secret being accessed
listAll bool // true to indicate listing all secrets in the org including node and user-level

msgPrinter *message.Printer
}
Expand Down Expand Up @@ -1298,7 +1300,7 @@ func (a *SecureAPI) secretsSetup(w http.ResponseWriter, r *http.Request) *Secret
}
}

return &SecretRequestInfo{org, ec, exUser, user, node, vaultSecretName, msgPrinter}
return &SecretRequestInfo{org, ec, exUser, user, node, vaultSecretName, false, msgPrinter}
}

func parseSecretDetails(w http.ResponseWriter, r *http.Request, msgPrinter *message.Printer) *secrets.SecretDetails {
Expand Down Expand Up @@ -1445,6 +1447,57 @@ func (a *SecureAPI) orgSecrets(w http.ResponseWriter, r *http.Request) {
}
}

func (a *SecureAPI) allSecrets(w http.ResponseWriter, r *http.Request) {
info := a.secretsSetup(w, r)
if info == nil {
return
}

info.listAll = true

// handle API options
switch r.Method {
// swagger:operation LIST /org/{org}/allsecrets allSecrets
//
// List all secrets belonging to the org including node and user-level secrets.
//
// ---
// consumes:
// - application/json
// produces:
// - application/json
// responses:
// '200':
// description: "Success or no secrets found."
// type: array
// items: string
// '401':
// description: "Unauthenticated user."
// type: string
// '403':
// description: "Secrets permission denied to user."
// type: string
// '503':
// description: "Secret provider unavailable"
// type: string
// '500':
// description: "Invalid vault response"
// type: string
case "LIST":
if payload, err, httpCode := a.listVaultSecret(info); err != nil {
glog.Errorf(APIlogString(err.Error()))
writeResponse(w, err.Error(), httpCode)
} else {
writeResponse(w, payload, http.StatusOK)
}
case "OPTIONS":
w.Header().Set("Allow", "LIST, OPTIONS")
w.WriteHeader(http.StatusOK)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}

// handler for /org/<org>/secrets/<secret> - GET, LIST, PUT, POST, DELETE, OPTIONS
func (a *SecureAPI) orgSecret(w http.ResponseWriter, r *http.Request) {
// check the provided secret name, <secret> can sometimes bind to user/<user>
Expand Down Expand Up @@ -2174,7 +2227,10 @@ func (a *SecureAPI) listVaultSecret(info *SecretRequestInfo) (interface{}, error
if info.vaultSecretName == "" {
secretNames := make([]string, 0)
var err error
if info.user != "" && info.node != "" {
if info.listAll {
// listing all secrets in the org including node and user-level
secretNames, err = a.secretProvider.ListAllSecrets(info.ec.GetExchangeId(), info.ec.GetExchangeToken(), info.org, "")
} else if info.user != "" && info.node != "" {
// listing node user level secrets
secretNames, err = a.secretProvider.ListUserNodeSecrets(info.ec.GetExchangeId(), info.ec.GetExchangeToken(), info.org, info.node, userNodePath)
} else if info.node != "" {
Expand Down Expand Up @@ -2280,7 +2336,7 @@ func (a *SecureAPI) verifySecretNames(ec exchange.ExchangeContext, exUser string
}

// check if the bound secret exists or not{}
payload, err, _ := a.listVaultSecret(&SecretRequestInfo{nodeOrg, ec, exUser, secretUser, secretNode, shortSecretName, msgPrinter})
payload, err, _ := a.listVaultSecret(&SecretRequestInfo{nodeOrg, ec, exUser, secretUser, secretNode, shortSecretName, false, msgPrinter})
if err != nil {
return false, "", fmt.Errorf(msgPrinter.Sprintf("Error checking secret %v in the secret manager.", fullSecretName))
} else if payload != nil {
Expand Down
3 changes: 2 additions & 1 deletion cli/hzn.go
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,7 @@ Environment Variables:
smUserPw := smCmd.Flag("user-pw", msgPrinter.Sprintf("Horizon Exchange credentials to query secrets manager resources. The default is HZN_EXCHANGE_USER_AUTH environment variable. If you don't prepend it with the user's org, it will automatically be prepended with the value of the HZN_ORG_ID environment variable.")).Short('u').PlaceHolder("USER:PW").String()
smSecretCmd := smCmd.Command("secret", msgPrinter.Sprintf("List and manage secrets in the secrets manager."))
smSecretListCmd := smSecretCmd.Command("list | ls", msgPrinter.Sprintf("Display the names of the secrets in the secrets manager.")).Alias("ls").Alias("list")
smSecretListAll := smSecretListCmd.Flag("listAll", msgPrinter.Sprintf("List all secrets in the given org in the secrets manager.")).Short('a').Bool()
smSecretListNodeId := smSecretListCmd.Flag("nodeId", msgPrinter.Sprintf("The node id of the node secret to list. Include only if this secret is specific to a single node.")).Short('n').String()
smSecretListName := smSecretListCmd.Arg("secretName", msgPrinter.Sprintf("List just this one secret. Returns a boolean indicating the existence of the secret. This is the name of the secret used in the secrets manager. If the secret does not exist, returns with exit code 1.")).String()
smSecretAddCmd := smSecretCmd.Command("add", msgPrinter.Sprintf("Add a secret to the secrets manager."))
Expand Down Expand Up @@ -1508,7 +1509,7 @@ Environment Variables:
fdo.VoucherDownload(*fdoOrg, *fdoUserPw, *fdoVoucherDownloadDevice, *fdoVoucherDownloadFile, *fdoVoucherDownloadOverwrite)

case smSecretListCmd.FullCommand():
secret_manager.SecretList(*smOrg, *smUserPw, *smSecretListName, *smSecretListNodeId)
secret_manager.SecretList(*smOrg, *smUserPw, *smSecretListName, *smSecretListNodeId, *smSecretListAll)
case smSecretAddCmd.FullCommand():
secret_manager.SecretAdd(*smOrg, *smUserPw, *smSecretAddName, *smSecretAddNodeId, *smSecretAddFile, *smSecretAddKey, *smSecretAddDetail, *smSecretAddOverwrite)
case smSecretRemoveCmd.FullCommand():
Expand Down
11 changes: 8 additions & 3 deletions cli/secrets_manager/secrets_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func queryWithRetry(query func() int, retryCount, retryInterval int) (httpCode i
// If the name provided is a directory,
// - nodeId is empty: lists all the secrets in the directory
// - else: list all the node secrets for the given directory
func SecretList(org, credToUse, secretName, secretNodeId string) {
func SecretList(org, credToUse, secretName, secretNodeId string, listAll bool) {
// get message printer
msgPrinter := i18n.GetMessagePrinter()

Expand Down Expand Up @@ -101,8 +101,13 @@ func SecretList(org, credToUse, secretName, secretNodeId string) {
// org/secrets/user/{userId}/node/{nodeId}
// org/secrets/node/{nodeId}/test-password
// org/secrets/user/{userId}/node/{nodeId}/test-password
return cliutils.AgbotList("org"+cliutils.AddSlash(org)+"/secrets"+cliutils.AddSlash(secretName), cliutils.OrgAndCreds(org, credToUse),
[]int{200, 400, 401, 403, 404, 503, 504}, &resp)
if listAll {
return cliutils.AgbotList("org"+cliutils.AddSlash(org)+"/allsecrets", cliutils.OrgAndCreds(org, credToUse),
[]int{200, 400, 401, 403, 404, 503, 504}, &resp)
} else {
return cliutils.AgbotList("org"+cliutils.AddSlash(org)+"/secrets"+cliutils.AddSlash(secretName), cliutils.OrgAndCreds(org, credToUse),
[]int{200, 400, 401, 403, 404, 503, 504}, &resp)
}
}
retCode := queryWithRetry(listQuery, 3, 1)

Expand Down

0 comments on commit bd78a8d

Please sign in to comment.