auth

Paddy 2015-01-14 Parent:385ac6294cdc Child:e000b1c24fc0

115:fa8ee6a4507c Browse Files

Turn AddEndpoint into AddEndpoints. Because one is a special case of many, it makes sense to be able to add multiple endpoints in a single call to the database. So we've converted the AddEndpoint method into an AddEndpoints method and updated our tests appropriately. We also filled in the errors when creating a client through the API, and moved things around to optimize for the maximum number of errors returned in a single call.

authcode_test.go client.go client_test.go context.go oauth2_test.go

     1.1 --- a/authcode_test.go	Sat Jan 10 04:16:07 2015 -0500
     1.2 +++ b/authcode_test.go	Wed Jan 14 00:23:30 2015 -0500
     1.3 @@ -139,7 +139,7 @@
     1.4  	if err != nil {
     1.5  		t.Fatal("Can't store client:", err)
     1.6  	}
     1.7 -	err = testContext.AddEndpoint(client.ID, endpoint)
     1.8 +	err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
     1.9  	if err != nil {
    1.10  		t.Fatal("Can't store endpoint:", err)
    1.11  	}
     2.1 --- a/client.go	Sat Jan 10 04:16:07 2015 -0500
     2.2 +++ b/client.go	Wed Jan 14 00:23:30 2015 -0500
     2.3 @@ -8,6 +8,7 @@
     2.4  	"github.com/gorilla/mux"
     2.5  	"net/http"
     2.6  	"net/url"
     2.7 +	"strconv"
     2.8  	"time"
     2.9  
    2.10  	"code.secondbit.org/uuid.hg"
    2.11 @@ -38,6 +39,11 @@
    2.12  	ErrClientWebsiteNotURL = errors.New("client website must be a valid absolute URL")
    2.13  )
    2.14  
    2.15 +const (
    2.16 +	clientTypePublic       = "public"
    2.17 +	clientTypeConfidential = "confidential"
    2.18 +)
    2.19 +
    2.20  // Client represents a client that grants access
    2.21  // to the auth server, exchanging grants for tokens,
    2.22  // and tokens for access.
    2.23 @@ -190,7 +196,7 @@
    2.24  	deleteClient(id uuid.ID) error
    2.25  	listClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error)
    2.26  
    2.27 -	addEndpoint(client uuid.ID, endpoint Endpoint) error
    2.28 +	addEndpoints(client uuid.ID, endpoint []Endpoint) error
    2.29  	removeEndpoint(client, endpoint uuid.ID) error
    2.30  	checkEndpoint(client uuid.ID, endpoint string) (bool, error)
    2.31  	listEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error)
    2.32 @@ -271,10 +277,10 @@
    2.33  	return clients, nil
    2.34  }
    2.35  
    2.36 -func (m *memstore) addEndpoint(client uuid.ID, endpoint Endpoint) error {
    2.37 +func (m *memstore) addEndpoints(client uuid.ID, endpoints []Endpoint) error {
    2.38  	m.endpointLock.Lock()
    2.39  	defer m.endpointLock.Unlock()
    2.40 -	m.endpoints[client.String()] = append(m.endpoints[client.String()], endpoint)
    2.41 +	m.endpoints[client.String()] = append(m.endpoints[client.String()], endpoints...)
    2.42  	return nil
    2.43  }
    2.44  
    2.45 @@ -330,14 +336,17 @@
    2.46  }
    2.47  
    2.48  func CreateClientHandler(w http.ResponseWriter, r *http.Request, c Context) {
    2.49 +	errors := []requestError{}
    2.50  	username, password, ok := r.BasicAuth()
    2.51  	if !ok {
    2.52 -		// TODO(paddy): return error
    2.53 +		errors = append(errors, requestError{Slug: requestErrAccessDenied})
    2.54 +		encode(w, r, http.StatusUnauthorized, response{Errors: errors})
    2.55  		return
    2.56  	}
    2.57  	profile, err := authenticate(username, password, c)
    2.58  	if err != nil {
    2.59 -		// TODO(paddy): return error
    2.60 +		errors = append(errors, requestError{Slug: requestErrAccessDenied})
    2.61 +		encode(w, r, http.StatusUnauthorized, response{Errors: errors})
    2.62  		return
    2.63  	}
    2.64  	var req newClientReq
    2.65 @@ -347,31 +356,43 @@
    2.66  		encode(w, r, http.StatusBadRequest, invalidFormatResponse)
    2.67  		return
    2.68  	}
    2.69 -	secret := make([]byte, 32)
    2.70 -	_, err = rand.Read(secret)
    2.71 -	if err != nil {
    2.72 -		// TODO(paddy): return error
    2.73 +	if req.Type != clientTypePublic && req.Type != clientTypeConfidential {
    2.74 +		errors = append(errors, requestError{Slug: requestErrInvalidValue, Field: "/type"})
    2.75 +		encode(w, r, http.StatusBadRequest, response{Errors: errors})
    2.76  		return
    2.77  	}
    2.78  	client := Client{
    2.79  		ID:      uuid.NewID(),
    2.80 -		Secret:  hex.EncodeToString(secret),
    2.81  		OwnerID: profile.ID,
    2.82  		Name:    req.Name,
    2.83  		Logo:    req.Logo,
    2.84  		Website: req.Website,
    2.85  		Type:    req.Type,
    2.86  	}
    2.87 +	if client.Type == clientTypePublic {
    2.88 +		secret := make([]byte, 32)
    2.89 +		_, err = rand.Read(secret)
    2.90 +		if err != nil {
    2.91 +			encode(w, r, http.StatusInternalServerError, actOfGodResponse)
    2.92 +			return
    2.93 +		}
    2.94 +		client.Secret = hex.EncodeToString(secret)
    2.95 +	}
    2.96  	err = c.SaveClient(client)
    2.97  	if err != nil {
    2.98 -		// TODO(paddy): return error
    2.99 +		if err == ErrClientAlreadyExists {
   2.100 +			errors = append(errors, requestError{Slug: requestErrConflict, Field: "/id"})
   2.101 +			encode(w, r, http.StatusBadRequest, response{Errors: errors})
   2.102 +			return
   2.103 +		}
   2.104 +		encode(w, r, http.StatusInternalServerError, actOfGodResponse)
   2.105  		return
   2.106  	}
   2.107  	endpoints := []Endpoint{}
   2.108 -	for _, u := range req.Endpoints {
   2.109 +	for pos, u := range req.Endpoints {
   2.110  		uri, err := url.Parse(u)
   2.111  		if err != nil {
   2.112 -			// TODO(paddy): add error to response
   2.113 +			errors = append(errors, requestError{Slug: requestErrInvalidFormat, Field: "/endpoints/" + strconv.Itoa(pos)})
   2.114  			continue
   2.115  		}
   2.116  		endpoint := Endpoint{
   2.117 @@ -380,13 +401,14 @@
   2.118  			URI:      *uri,
   2.119  			Added:    time.Now(),
   2.120  		}
   2.121 -		err = c.AddEndpoint(client.ID, endpoint)
   2.122 -		if err != nil {
   2.123 -			// TODO(paddy): return error
   2.124 -			return
   2.125 -		}
   2.126  		endpoints = append(endpoints, endpoint)
   2.127  	}
   2.128 +	err = c.AddEndpoints(client.ID, endpoints)
   2.129 +	if err != nil {
   2.130 +		errors = append(errors, requestError{Slug: requestErrActOfGod})
   2.131 +		encode(w, r, http.StatusInternalServerError, response{Errors: errors, Clients: []Client{client}})
   2.132 +		return
   2.133 +	}
   2.134  	resp := response{
   2.135  		Clients:   []Client{client},
   2.136  		Endpoints: endpoints,
     3.1 --- a/client_test.go	Sat Jan 10 04:16:07 2015 -0500
     3.2 +++ b/client_test.go	Wed Jan 14 00:23:30 2015 -0500
     3.3 @@ -155,7 +155,7 @@
     3.4  		if err != nil {
     3.5  			t.Fatalf("Error saving client to %T: %s", store, err)
     3.6  		}
     3.7 -		err = store.addEndpoint(client.ID, endpoint1)
     3.8 +		err = store.addEndpoints(client.ID, []Endpoint{endpoint1})
     3.9  		if err != nil {
    3.10  			t.Fatalf("Error adding endpoint to client in %T: %s", store, err)
    3.11  		}
    3.12 @@ -170,7 +170,7 @@
    3.13  		if !success {
    3.14  			t.Fatalf("Expected field %s to be %v, but %T returned %v", field, expectation, store, result)
    3.15  		}
    3.16 -		err = store.addEndpoint(client.ID, endpoint2)
    3.17 +		err = store.addEndpoints(client.ID, []Endpoint{endpoint2})
    3.18  		if err != nil {
    3.19  			t.Fatalf("Error adding endpoint to client in %T: %s", store, err)
    3.20  		}
    3.21 @@ -331,11 +331,11 @@
    3.22  		if err != nil {
    3.23  			t.Fatalf("Error saving client in %T: %s", store, err)
    3.24  		}
    3.25 -		err = store.addEndpoint(client.ID, endpoint1)
    3.26 +		err = store.addEndpoints(client.ID, []Endpoint{endpoint1})
    3.27  		if err != nil {
    3.28  			t.Fatalf("Error saving endpoint in %T: %s", store, err)
    3.29  		}
    3.30 -		err = store.addEndpoint(client.ID, endpoint2)
    3.31 +		err = store.addEndpoints(client.ID, []Endpoint{endpoint2})
    3.32  		if err != nil {
    3.33  			t.Fatalf("Error saving endpoint in %T: %s", store, err)
    3.34  		}
    3.35 @@ -393,11 +393,11 @@
    3.36  		if err != nil {
    3.37  			t.Fatalf("Error saving client in %T: %s", store, err)
    3.38  		}
    3.39 -		err = store.addEndpoint(client.ID, endpoint1)
    3.40 +		err = store.addEndpoints(client.ID, []Endpoint{endpoint1})
    3.41  		if err != nil {
    3.42  			t.Fatalf("Error saving endpoint in %T: %s", store, err)
    3.43  		}
    3.44 -		err = store.addEndpoint(client.ID, endpoint2)
    3.45 +		err = store.addEndpoints(client.ID, []Endpoint{endpoint2})
    3.46  		if err != nil {
    3.47  			t.Fatalf("Error saving endpoint in %T: %s", store, err)
    3.48  		}
     4.1 --- a/context.go	Sat Jan 10 04:16:07 2015 -0500
     4.2 +++ b/context.go	Wed Jan 14 00:23:30 2015 -0500
     4.3 @@ -107,13 +107,13 @@
     4.4  	return c.clients.listClientsByOwner(ownerID, num, offset)
     4.5  }
     4.6  
     4.7 -// AddEndpoint stores the specified Endpoint in the clientStore associated with the Context,
     4.8 -// and associates the newly-stored Endpoint with the Client specified by the passed ID.
     4.9 -func (c Context) AddEndpoint(client uuid.ID, endpoint Endpoint) error {
    4.10 +// AddEndpoints stores the specified Endpoints in the clientStore associated with the Context,
    4.11 +// and associates the newly-stored Endpoints with the Client specified by the passed ID.
    4.12 +func (c Context) AddEndpoints(client uuid.ID, endpoints []Endpoint) error {
    4.13  	if c.clients == nil {
    4.14  		return ErrNoClientStore
    4.15  	}
    4.16 -	return c.clients.addEndpoint(client, endpoint)
    4.17 +	return c.clients.addEndpoints(client, endpoints)
    4.18  }
    4.19  
    4.20  // RemoveEndpoint deletes the Endpoint with the specified ID from the clientStore associated
     5.1 --- a/oauth2_test.go	Sat Jan 10 04:16:07 2015 -0500
     5.2 +++ b/oauth2_test.go	Wed Jan 14 00:23:30 2015 -0500
     5.3 @@ -60,7 +60,7 @@
     5.4  	if err != nil {
     5.5  		t.Fatal("Can't store client:", err)
     5.6  	}
     5.7 -	err = testContext.AddEndpoint(client.ID, endpoint)
     5.8 +	err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
     5.9  	if err != nil {
    5.10  		t.Fatal("Can't store endpoint:", err)
    5.11  	}
    5.12 @@ -287,7 +287,7 @@
    5.13  		URI:      *uri,
    5.14  		Added:    time.Now(),
    5.15  	}
    5.16 -	err = testContext.AddEndpoint(client.ID, endpoint)
    5.17 +	err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
    5.18  	if err != nil {
    5.19  		t.Fatal("Can't store endpoint:", err)
    5.20  	}
    5.21 @@ -307,7 +307,7 @@
    5.22  		URI:      *uri,
    5.23  		Added:    time.Now(),
    5.24  	}
    5.25 -	err = testContext.AddEndpoint(client.ID, endpoint2)
    5.26 +	err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint2})
    5.27  	if err != nil {
    5.28  		t.Fatal("Can't store endpoint:", err)
    5.29  	}
    5.30 @@ -367,7 +367,7 @@
    5.31  	if err != nil {
    5.32  		t.Fatal("Can't store client:", err)
    5.33  	}
    5.34 -	err = testContext.AddEndpoint(client.ID, endpoint)
    5.35 +	err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
    5.36  	if err != nil {
    5.37  		t.Fatal("Can't store endpoint:", err)
    5.38  	}
    5.39 @@ -474,7 +474,7 @@
    5.40  	if err != nil {
    5.41  		t.Fatal("Can't store client:", err)
    5.42  	}
    5.43 -	err = testContext.AddEndpoint(client.ID, endpoint)
    5.44 +	err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
    5.45  	if err != nil {
    5.46  		t.Fatal("Can't store endpoint:", err)
    5.47  	}