Skip to content

Commit

Permalink
Merge pull request #10 from rabobank/secrets-maintenance-extensions
Browse files Browse the repository at this point in the history
Secrets maintenance extensions
  • Loading branch information
jcvrabo committed Aug 11, 2023
2 parents 4a420c2 + 780af07 commit 4ec5910
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 15 deletions.
85 changes: 76 additions & 9 deletions controllers/ApiController.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ func deleteKey(credentials map[string]interface{}, parts []string) bool {
if len(parts) > 1 {
// the key has more parts, let's check if it's a map
if subMap, isMap := value.(map[string]interface{}); isMap {
return deleteKey(subMap, parts[1:])
if deleteKey(subMap, parts[1:]) {
if len(subMap) == 0 {
delete(credentials, parts[0])
}
return true
}
}
} else {
delete(credentials, parts[0])
Expand Down Expand Up @@ -93,12 +98,12 @@ func deleteKeys(credentials map[string]interface{}, keysToDelete []string) ([]st

func ListServiceKeys(w http.ResponseWriter, r *http.Request) {
if username, serviceInstanceId, ok := userAndService(w, r); ok {
fmt.Printf("[API] %s updating keys of service %s\n", username, serviceInstanceId)
if data, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId)); e != nil {
fmt.Printf("[API] %s Listing keys of service %s\n", username, serviceInstanceId)
if data, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId), 0); e != nil {
fmt.Printf("Unable to get service %s credentials, deeming it a bad request.\n", serviceInstanceId)
util.WriteHttpResponse(w, http.StatusBadRequest, nil)
} else if len(data.Data) == 0 {
fmt.Printf("[API] %s trying to update keys for non-existing credhub service %s\n", username, serviceInstanceId)
fmt.Printf("[API] %s trying to list keys for non-existing credhub service %s\n", username, serviceInstanceId)
util.WriteHttpResponse(w, http.StatusNotFound, "Not Found")
} else if credentials, isType := data.Data[0].Value.(map[string]interface{}); isType {
keys := listMapKeys(credentials, nil, "")
Expand All @@ -110,14 +115,46 @@ func ListServiceKeys(w http.ResponseWriter, r *http.Request) {
}
}

func ListServiceVersions(w http.ResponseWriter, r *http.Request) {
if username, serviceInstanceId, ok := userAndService(w, r); ok {
fmt.Printf("[API] %s listing credential versions for service %s\n", username, serviceInstanceId)
if data, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId), 20); e != nil {
fmt.Printf("Unable to get service %s versions, deeming it a bad request.\n", serviceInstanceId)
util.WriteHttpResponse(w, http.StatusBadRequest, nil)
} else if len(data.Data) == 0 {
fmt.Printf("[API] %s trying to list versions for non-existing credhub service %s\n", username, serviceInstanceId)
util.WriteHttpResponse(w, http.StatusNotFound, "Not Found")
} else {
versions := make([]model.SecretsVersionKeys, 0)
for _, entry := range data.Data {
if credentials, isType := entry.Value.(map[string]interface{}); isType {
versions = append(versions, model.SecretsVersionKeys{
VersionCreatedAt: entry.VersionCreatedAt,
ID: entry.ID,
Keys: listMapKeys(credentials, nil, ""),
})
} else {
fmt.Printf("version %s from service %s is not a json map object. Skipping it.\n", entry.ID, serviceInstanceId)
}
}

if len(versions) > 0 {
util.WriteHttpResponse(w, http.StatusOK, versions)
} else {
util.WriteHttpResponse(w, http.StatusNotFound, "credentials don't have any valid versions")
}
}
}
}

func UpdateServiceKeys(w http.ResponseWriter, r *http.Request) {
if username, serviceInstanceId, ok := userAndService(w, r); ok {
fmt.Printf("[API] %s updating keys of service %s\n", username, serviceInstanceId)
if data, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId)); e != nil {
if data, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId), 0); e != nil {
fmt.Printf("Unable to get service %s credentials, deeming it a bad request.\n", serviceInstanceId)
util.WriteHttpResponse(w, http.StatusBadRequest, nil)
} else if len(data.Data) == 0 {
fmt.Printf("[API] %s trying to list keys for non-existing credhub service %s\n", username, serviceInstanceId)
fmt.Printf("[API] %s trying to update keys for non-existing credhub service %s\n", username, serviceInstanceId)
util.WriteHttpResponse(w, http.StatusNotFound, "Not Found")
} else if credentials, isType := data.Data[0].Value.(map[string]interface{}); isType {
updatedValues := make(map[string]interface{})
Expand All @@ -144,7 +181,7 @@ func UpdateServiceKeys(w http.ResponseWriter, r *http.Request) {
func DeleteServiceKeys(w http.ResponseWriter, r *http.Request) {
if username, serviceInstanceId, ok := userAndService(w, r); ok {
fmt.Printf("[API] %s deleting keys from service %s\n", username, serviceInstanceId)
if data, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId)); e != nil {
if data, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId), 0); e != nil {
fmt.Printf("Unable to get service %s credentials, deeming it a bad request.\n", serviceInstanceId)
util.WriteHttpResponse(w, http.StatusBadRequest, nil)
} else if len(data.Data) == 0 {
Expand All @@ -157,7 +194,7 @@ func DeleteServiceKeys(w http.ResponseWriter, r *http.Request) {
util.WriteHttpResponse(w, http.StatusBadRequest, "Unable to process json array")
} else {
ignoredKeys, keysHaveBeenDeleted := deleteKeys(credentials, keysToDelete)
response := struct{ IgnoredKeys []string }{ignoredKeys}
response := model.DeleteResponse{IgnoredKeys: ignoredKeys}

if keysHaveBeenDeleted {
if e = credhub.SetCredhubJson(model.CredhubJsonRequest{Type: "json", Name: credentialsPath.ServiceInstanceId(serviceInstanceId), Value: credentials}); e != nil {
Expand All @@ -168,7 +205,7 @@ func DeleteServiceKeys(w http.ResponseWriter, r *http.Request) {
util.WriteHttpResponse(w, http.StatusAccepted, response)
}
} else {
util.WriteHttpResponse(w, http.StatusNotFound, response)
util.WriteHttpResponse(w, http.StatusNotModified, response)
}
}
} else {
Expand All @@ -177,3 +214,33 @@ func DeleteServiceKeys(w http.ResponseWriter, r *http.Request) {
}
}
}

func ReinstateServiceVersion(w http.ResponseWriter, r *http.Request) {
if username, serviceInstanceId, ok := userAndService(w, r); ok {
versionId := mux.Vars(r)["version_id"]
fmt.Printf("[API] %s reinstating credential version %s for service %s\n", username, versionId, serviceInstanceId)

if data, e := credhub.GetCredhubDataVersion(versionId); e != nil {
fmt.Printf("Unable to get version %s, deeming it a bad request: %v\n", versionId, e)
util.WriteHttpResponse(w, http.StatusBadRequest, nil)
} else if !strings.HasPrefix(data.Name, credentialsPath.ServiceInstanceId(serviceInstanceId)) {
fmt.Printf("[API] %s trying to reinstate a version to service %s from another service.\n", username, serviceInstanceId)
util.WriteHttpResponse(w, http.StatusBadRequest, nil)
} else if currentData, e := credhub.GetCredhubData(credentialsPath.ServiceInstanceId(serviceInstanceId), 0); e != nil {
fmt.Printf("Unable to get service %s credentials, deeming it a bad request.\n", serviceInstanceId)
util.WriteHttpResponse(w, http.StatusBadRequest, nil)
} else if len(currentData.Data) == 0 {
fmt.Printf("[API] %s trying to reinstate a version for a non-existing credhub service %s\n", username, serviceInstanceId)
util.WriteHttpResponse(w, http.StatusNotFound, "Not Found")
} else if currentData.Data[0].ID == versionId {
fmt.Printf("Credentials version %s being reinstated for service %s is already the current one", versionId, serviceInstanceId)
util.WriteHttpResponse(w, http.StatusNotModified, "Unmodified")
} else if e = credhub.SetCredhubJson(model.CredhubJsonRequest{Type: "json", Name: credentialsPath.ServiceInstanceId(serviceInstanceId), Value: data.Value.(map[string]interface{})}); e != nil {
fmt.Printf("Failed to submit credentials update to credhub: %v\n", e)
util.WriteHttpResponse(w, http.StatusInternalServerError, "Failed to update service")
} else {
fmt.Printf("[API] %s has updated credentials in service %s\n", username, serviceInstanceId)
util.WriteHttpResponse(w, http.StatusAccepted, "Credentials Updated")
}
}
}
2 changes: 1 addition & 1 deletion controllers/ServiceBindingController.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func DeleteServiceBinding(w http.ResponseWriter, r *http.Request) {
serviceBindingId := mux.Vars(r)["service_binding_guid"]
fmt.Printf("delete service binding id %s for service instance id %s...\n", serviceBindingId, serviceInstanceId)
credhubPath := fmt.Sprintf("/pcsb/%s/%s", serviceInstanceId, serviceBindingId)
credhubEntry, err := credhub.GetCredhubData(credhubPath)
credhubEntry, err := credhub.GetCredhubData(credhubPath, 0)
if err != nil {
util.WriteHttpResponse(w, http.StatusOK, model.BrokerError{Error: "FAILED", Description: fmt.Sprintf("failed to read credhub at path %s: %s", credhubPath, err.Error()), InstanceUsable: false, UpdateRepeatable: false})
return
Expand Down
5 changes: 3 additions & 2 deletions controllers/ServiceInstanceController.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package controllers

import (
"fmt"
"net/http"

"github.com/gorilla/mux"
"github.com/rabobank/credhub-service-broker/conf"
"github.com/rabobank/credhub-service-broker/credhub"
"github.com/rabobank/credhub-service-broker/model"
"github.com/rabobank/credhub-service-broker/util"
"net/http"
)

func Catalog(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -45,7 +46,7 @@ func CreateOrUpdateServiceInstance(w http.ResponseWriter, r *http.Request) {
func DeleteServiceInstance(w http.ResponseWriter, r *http.Request) {
serviceInstanceId := mux.Vars(r)["service_instance_guid"]
fmt.Printf("delete service instance %s...\n", serviceInstanceId)
_, err := credhub.GetCredhubData(fmt.Sprintf("/pcsb/%s/credentials", serviceInstanceId))
_, err := credhub.GetCredhubData(fmt.Sprintf("/pcsb/%s/credentials", serviceInstanceId), 0)
if err != nil {
util.WriteHttpResponse(w, http.StatusGone, err)
return
Expand Down
27 changes: 25 additions & 2 deletions credhub/Client.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ func setCredhub(jsonBytes []byte) error {
return err
}

func GetCredhubData(credhubPath string) (model.CredhubEntry, error) {
func GetCredhubData(credhubPath string, versions int) (model.CredhubEntry, error) {
var err error
var credhubEntry model.CredhubEntry
var resp *http.Response
path := fmt.Sprintf("/api/v1/data?name=%s&current=true", credhubPath)
versionsParameter := "current=true"
if versions > 1 {
versionsParameter = fmt.Sprintf("versions=%d", versions)
}
path := fmt.Sprintf("/api/v1/data?name=%s&%s", credhubPath, versionsParameter)
client, req, err := getHttpClientAndRequest(http.MethodGet, path, nil)
if err == nil {
resp, err = client.Do(req)
Expand All @@ -95,6 +99,25 @@ func GetCredhubData(credhubPath string) (model.CredhubEntry, error) {
return credhubEntry, err
}

func GetCredhubDataVersion(version string) (model.CredhubDataResponse, error) {
var err error
var credhubData model.CredhubDataResponse
var resp *http.Response
path := fmt.Sprintf("/api/v1/data/%s", version)
client, req, err := getHttpClientAndRequest(http.MethodGet, path, nil)
if err == nil {
resp, err = client.Do(req)
if err == nil && resp != nil && resp.StatusCode == http.StatusOK {
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if err = json.Unmarshal(body, &credhubData); err != nil {
return credhubData, errors.New(fmt.Sprintf("cannot unmarshal JSON response from %s: %s\n", conf.CredhubURL+path, err))
}
}
}
return credhubData, err
}

func DeleteCredhubData(credhubPath string) error {
var err error
var resp *http.Response
Expand Down
15 changes: 15 additions & 0 deletions model/Api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package model

import (
"time"
)

type DeleteResponse struct {
IgnoredKeys []string `json:"ignored"`
}

type SecretsVersionKeys struct {
VersionCreatedAt time.Time `json:"timestamp"`
ID string `json:"id"`
Keys []string `json:"keys"`
}
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion security/uaa.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func UAA(request *http.Request) (bool, *string) {
return false, nil
}

// let's see if there's a service instance id to authroize the user
// let's see if there's a service instance id to authorize the user
if serviceInstanceId, found := mux.Vars(request)["service_instance_guid"]; !found {
fmt.Printf("[AUTH] Invalid endpoint requested by %s: %s\n", tokenIntrospection.UserName, request.URL.Path)
} else {
Expand Down
2 changes: 2 additions & 0 deletions server/HttpServer.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func StartServer() {
router.HandleFunc("/api/{service_instance_guid}/keys", controllers.ListServiceKeys).Methods("GET")
router.HandleFunc("/api/{service_instance_guid}/keys", controllers.UpdateServiceKeys).Methods("PUT")
router.HandleFunc("/api/{service_instance_guid}/keys", controllers.DeleteServiceKeys).Methods("DELETE")
router.HandleFunc("/api/{service_instance_guid}/versions", controllers.ListServiceVersions).Methods("GET")
router.HandleFunc("/api/{service_instance_guid}/version/{version_id}", controllers.ReinstateServiceVersion).Methods("PUT")

http.Handle("/", router)

Expand Down

0 comments on commit 4ec5910

Please sign in to comment.