auth
131:f474ce964dcf Browse Files
Implement handlers for retrieving clients. Create a GetClientHandler and ListClientsHandler for retrieving details about a client. Currently, we're not returning the client secret for these clients. We're also not doing any auth. We may want to restrict auth to the owner of the clients, and return secrets only when auth'd, and maybe even only when a special header is included. Alternatively, we could only return the secret when retrieving a single client. Still unsure how I want to handle that.
1.1 --- a/client.go Sat Jan 24 09:48:12 2015 -0500 1.2 +++ b/client.go Sat Jan 24 10:34:33 2015 -0500 1.3 @@ -56,10 +56,12 @@ 1.4 ) 1.5 1.6 const ( 1.7 - clientTypePublic = "public" 1.8 - clientTypeConfidential = "confidential" 1.9 - minClientNameLen = 2 1.10 - maxClientNameLen = 24 1.11 + clientTypePublic = "public" 1.12 + clientTypeConfidential = "confidential" 1.13 + minClientNameLen = 2 1.14 + maxClientNameLen = 24 1.15 + defaultClientResponseSize = 20 1.16 + maxClientResponseSize = 50 1.17 1.18 normalizeFlags = purell.FlagsUsuallySafeNonGreedy | purell.FlagSortQuery 1.19 ) 1.20 @@ -386,8 +388,8 @@ 1.21 1.22 func RegisterClientHandlers(r *mux.Router, context Context) { 1.23 r.Handle("/clients", wrap(context, CreateClientHandler)).Methods("POST") 1.24 - // BUG(paddy): We need to implement a handler to retrieve info on a client. 1.25 - // BUG(paddy): We need to implement a handler to list clients. 1.26 + r.Handle("/clients", wrap(context, ListClientsHandler)).Methods("GET") 1.27 + r.Handle("/clients/{id}", wrap(context, GetClientHandler)).Methods("GET") 1.28 // BUG(paddy): We need to implement a handler to update a client. 1.29 // BUG(paddy): We need to implement a handler to delete a client. Also, what should that do with the grants and tokens belonging to that client? 1.30 // BUG(paddy): We need to implement a handler to add an endpoint to a client. 1.31 @@ -492,6 +494,95 @@ 1.32 encode(w, r, http.StatusCreated, resp) 1.33 } 1.34 1.35 +func GetClientHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.36 + errors := []requestError{} 1.37 + vars := mux.Vars(r) 1.38 + if vars["id"] == "" { 1.39 + errors = append(errors, requestError{Slug: requestErrMissing, Param: "id"}) 1.40 + encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.41 + return 1.42 + } 1.43 + id, err := uuid.Parse(vars["id"]) 1.44 + if err != nil { 1.45 + errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "id"}) 1.46 + encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.47 + return 1.48 + } 1.49 + client, err := c.GetClient(id) 1.50 + if err != nil { 1.51 + if err == ErrClientNotFound { 1.52 + errors = append(errors, requestError{Slug: requestErrNotFound, Param: "id"}) 1.53 + encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.54 + return 1.55 + } 1.56 + errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.57 + encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.58 + return 1.59 + } 1.60 + client.Secret = "" 1.61 + // BUG(paddy): How should auth be handled for retrieving clients? 1.62 + resp := response{ 1.63 + Clients: []Client{client}, 1.64 + Errors: errors, 1.65 + } 1.66 + encode(w, r, http.StatusOK, resp) 1.67 +} 1.68 + 1.69 +func ListClientsHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.70 + errors := []requestError{} 1.71 + var err error 1.72 + // BUG(paddy): If ids are provided in query params, retrieve only those clients 1.73 + // BUG(paddy): We should have auth when listing clients 1.74 + num := defaultClientResponseSize 1.75 + offset := 0 1.76 + ownerIDStr := r.URL.Query().Get("owner_id") 1.77 + numStr := r.URL.Query().Get("num") 1.78 + offsetStr := r.URL.Query().Get("offset") 1.79 + if numStr != "" { 1.80 + num, err = strconv.Atoi(numStr) 1.81 + if err != nil { 1.82 + errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "num"}) 1.83 + } 1.84 + if num > maxClientResponseSize { 1.85 + errors = append(errors, requestError{Slug: requestErrOverflow, Param: "num"}) 1.86 + } 1.87 + } 1.88 + if offsetStr != "" { 1.89 + offset, err = strconv.Atoi(offsetStr) 1.90 + if err != nil { 1.91 + errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "offset"}) 1.92 + } 1.93 + } 1.94 + if ownerIDStr == "" { 1.95 + errors = append(errors, requestError{Slug: requestErrMissing, Param: "owner_id"}) 1.96 + } 1.97 + if len(errors) > 0 { 1.98 + encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.99 + return 1.100 + } 1.101 + ownerID, err := uuid.Parse(ownerIDStr) 1.102 + if err != nil { 1.103 + errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "owner_id"}) 1.104 + encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.105 + return 1.106 + } 1.107 + clients, err := c.ListClientsByOwner(ownerID, num, offset) 1.108 + if err != nil { 1.109 + errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.110 + encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.111 + return 1.112 + } 1.113 + for pos, client := range clients { 1.114 + client.Secret = "" 1.115 + clients[pos] = client 1.116 + } 1.117 + resp := response{ 1.118 + Clients: clients, 1.119 + Errors: errors, 1.120 + } 1.121 + encode(w, r, http.StatusOK, resp) 1.122 +} 1.123 + 1.124 func clientCredentialsValidate(w http.ResponseWriter, r *http.Request, context Context) (scope string, profileID uuid.ID, valid bool) { 1.125 scope = r.PostFormValue("scope") 1.126 valid = true
2.1 --- a/client_test.go Sat Jan 24 09:48:12 2015 -0500 2.2 +++ b/client_test.go Sat Jan 24 10:34:33 2015 -0500 2.3 @@ -782,3 +782,5 @@ 2.4 } 2.5 2.6 // BUG(paddy): We need to test the clientCredentialsValidate function. 2.7 +// BUG(paddy): We need to test the GetClientHandler. 2.8 +// BUG(paddy): We need to test the ListClientsHandler.