Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 9 additions & 47 deletions assets/credhub-service-broker/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
"time"

"code.cloudfoundry.org/credhub-cli/credhub"
"code.cloudfoundry.org/credhub-cli/credhub/credentials"
"code.cloudfoundry.org/credhub-cli/credhub/credentials/values"
"code.cloudfoundry.org/credhub-cli/credhub/permissions"
"github.com/go-chi/chi/v5"
uuid "github.com/satori/go.uuid"
)
Expand All @@ -29,57 +31,17 @@ func init() {
PLAN_UUID = uuid.NewV4().String()
}

func catalogHandler(w http.ResponseWriter, r *http.Request) {
// Create a new catalog response
type Plans struct {
Name string `json:"name"`
ID string `json:"id"`
Description string `json:"description"`
}
type Services struct {
Name string `json:"name"`
ID string `json:"id"`
Description string `json:"description"`
Bindable bool `json:"bindable"`
Plans []Plans `json:"plans"`
}
catalog := struct {
Services []Services `json:"services"`
}{
Services: []Services{
{
Name: SERVICE_NAME,
ID: SERVICE_UUID,
Description: "credhub read service for tests",
Bindable: true,
Plans: []Plans{
{
Name: "credhub-read-plan",
ID: PLAN_UUID,
Description: "credhub read plan for tests",
},
},
},
},
}

// Marshal the catalog response to JSON
catalogJSON, err := json.Marshal(catalog)
if err != nil {
log.Println("Failed to marshal catalog response: ", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

// Write the catalog response to the response writer
w.WriteHeader(http.StatusOK)
w.Write(catalogJSON) //nolint:errcheck
type CredhubClient interface {
SetJSON(name string, value values.JSON, options ...credhub.SetOption) (credentials.JSON, error)
AddPermission(path string, actor string, ops []string) (*permissions.Permission, error)
Delete(name string) error
}

func bindHandler(ch *credhub.CredHub, bindings map[string]string) http.HandlerFunc {
func bindHandler(ch CredhubClient, bindings map[string]string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Parse URL parameters
sbGUID := chi.URLParam(r, "service_binding_guid")
sbGUID := r.PathValue("binding_id")
// sbGUID := chi.URLParam(r, "service_binding_guid")
if sbGUID == "" {
log.Println("Missing service binding GUID")
w.WriteHeader(http.StatusBadRequest)
Expand Down
86 changes: 86 additions & 0 deletions assets/credhub-service-broker/handlers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package main

import (
"net/http"
"net/http/httptest"
"strings"
"testing"

"code.cloudfoundry.org/credhub-cli/credhub"
"code.cloudfoundry.org/credhub-cli/credhub/credentials"
"code.cloudfoundry.org/credhub-cli/credhub/credentials/values"
"code.cloudfoundry.org/credhub-cli/credhub/permissions"
)

type mockCredhubClient struct {
SetJSONReturn credentials.JSON
SetJSONReturnErr error
AddPermissionReturn *permissions.Permission
AddPermissionReturnErr error
}

func (m *mockCredhubClient) SetJSON(name string, value values.JSON, options ...credhub.SetOption) (credentials.JSON, error) {
return m.SetJSONReturn, m.SetJSONReturnErr
}

func (m *mockCredhubClient) AddPermission(path string, actor string, ops []string) (*permissions.Permission, error) {
return m.AddPermissionReturn, m.AddPermissionReturnErr
}

func (m *mockCredhubClient) Delete(name string) error {
return nil
}

func TestBindings_Add(t *testing.T) {
t.Parallel()

tests := []struct {
name string
bindingID string
body string
setJSONReturn credentials.JSON
setJSONReturnErr error
expectedStatus int
expectedBody string
}{
{
name: "simple",
bindingID: "test-binding-id",
body: `{"app_guid": "test-app-guid"}`,
setJSONReturn: credentials.JSON{Base: credentials.Base{Name: "test-credhub"}},
expectedStatus: http.StatusCreated,
expectedBody: `{"credentials":{"credhub-ref":"test-credhub"}}`,
},
{
name: "no-service-binding-id",
expectedStatus: http.StatusBadRequest,
},
}

for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

mcc := &mockCredhubClient{
SetJSONReturn: tc.setJSONReturn,
SetJSONReturnErr: tc.setJSONReturnErr,
}

req := httptest.NewRequest("PUT", "/v2/service_instances/test-guid/service_bindings/test-binding-guid", strings.NewReader(tc.body))
req.SetPathValue("binding_id", tc.bindingID)

rr := httptest.NewRecorder()
hf := bindHandler(mcc, map[string]string{})
hf.ServeHTTP(rr, req)

if status := rr.Code; status != tc.expectedStatus {
t.Errorf("handler returned wrong status code: got %v want %v", status, tc.expectedStatus)
}

if rr.Body.String() != tc.expectedBody {
t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), tc.expectedBody)
}
})
}
}
112 changes: 112 additions & 0 deletions assets/credhub-service-broker/internal/bindings/bindings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package bindings

import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"

"code.cloudfoundry.org/credhub-cli/credhub"
"code.cloudfoundry.org/credhub-cli/credhub/credentials"
"code.cloudfoundry.org/credhub-cli/credhub/credentials/values"
"code.cloudfoundry.org/credhub-cli/credhub/permissions"
)

type CredhubClient interface {
SetJSON(name string, value values.JSON, options ...credhub.SetOption) (credentials.JSON, error)
AddPermission(path string, actor string, ops []string) (*permissions.Permission, error)
Delete(name string) error
}

type Bindings struct {
m map[string]string
cc CredhubClient
}

func New(cc CredhubClient) *Bindings {
return &Bindings{
m: make(map[string]string),
cc: cc,
}
}

type BindingRequest struct {
AppGuid string `json:"app_guid"`
}

type Credentials struct {
CredhubRef string `json:"credhub-ref"`
}

type BindingResponse struct {
Credentials Credentials `json:"credentials"`
}

func (b *Bindings) Add(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("binding_id")
if id == "" {
log.Panicf("Path does not include binding id")
}

var br BindingRequest
err := json.NewDecoder(r.Body).Decode(&br)
r.Body.Close()
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprintf("Failed to parse binding request: %s", err.Error())))
return
}

name := strconv.FormatInt(time.Now().UnixNano(), 10)
cred, err := b.cc.SetJSON(name, values.JSON{
"user-name": "pinkyPie",
"password": "rainbowDash",
})
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Failed to set credential: %s", err.Error())))
return
}

b.m[id] = cred.Name

if br.AppGuid != "" {
_, err = b.cc.AddPermission(cred.Name, "mtls-app:"+br.AppGuid, []string{"read"})
if err != nil {
log.Println("Failed to add permission: ", err)
}
}

resp := BindingResponse{
Credentials: Credentials{
CredhubRef: "test-credhub",
},
}
respJSON, err := json.Marshal(resp)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Failed to marshal binding response: %s", err.Error())))
return
}

w.WriteHeader(http.StatusCreated)
w.Write(respJSON)
}

func (b *Bindings) Remove(w http.ResponseWriter, r *http.Request) {
// id := r.PathValue("id")

w.Write([]byte("{}"))
}

func (b *Bindings) Get(id string) (string, bool) {
resp, ok := b.m[id]
return resp, ok
}

func (b Bindings) Register(mux *http.ServeMux) {
mux.HandleFunc("/v2/service_instances/{id}/service_bindings/{binding_id}", b.Add)
mux.HandleFunc("/v2/service_instances/{id}/service_bindings/{binding_id}", b.Remove)
}
Loading