Skip to content
This repository has been archived by the owner on May 19, 2020. It is now read-only.

Commit

Permalink
Merge pull request #415 from 18F/remove_priv_calls
Browse files Browse the repository at this point in the history
Remove privileged proxy for converting ids between e-mail addresses
  • Loading branch information
Marco Segreto committed Jul 1, 2016
2 parents 1b9e9d0 + 0e218bb commit cba601c
Show file tree
Hide file tree
Showing 5 changed files with 0 additions and 214 deletions.
1 change: 0 additions & 1 deletion controllers/routes.go
Expand Up @@ -44,7 +44,6 @@ func InitRouter(settings *helpers.Settings) *web.Router {
uaaRouter := secureRouter.Subrouter(UAAContext{}, "/uaa")
uaaRouter.Middleware((*UAAContext).OAuth)
uaaRouter.Get("/userinfo", (*UAAContext).UserInfo)
uaaRouter.Post("/Users", (*UAAContext).QueryUser)

// Setup the /log subrouter.
logRouter := secureRouter.Subrouter(LogContext{}, "/log")
Expand Down
11 changes: 0 additions & 11 deletions controllers/secure.go
Expand Up @@ -44,17 +44,6 @@ func (c *SecureContext) Proxy(rw http.ResponseWriter, req *http.Request, url str
c.submitRequest(rw, req, url, client, responseHandler)
}

// PrivilegedProxy is an internal function that will construct the client using
// the credentials of the web app itself (not of the user) with the token in the headers and
// then sends a request.
func (c *SecureContext) PrivilegedProxy(rw http.ResponseWriter, req *http.Request, url string) {
// Acquire the http client and the refresh token if needed
// https://godoc.org/golang.org/x/oauth2#Config.Client
client := c.Settings.HighPrivilegedOauthConfig.Client(c.Settings.TokenContext)
c.submitRequest(rw, req, url, client, c.GenericResponseHandler)

}

// submitRequest uses a given client and submits the specified request.
func (c *SecureContext) submitRequest(rw http.ResponseWriter, req *http.Request, url string, client *http.Client, responseHandler ResponseHandler) {
// Prevents lingering goroutines from living forever.
Expand Down
60 changes: 0 additions & 60 deletions controllers/secure_test.go
Expand Up @@ -9,9 +9,7 @@ import (
"golang.org/x/oauth2"

"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
Expand Down Expand Up @@ -114,61 +112,3 @@ func TestProxy(t *testing.T) {
testServer.Close()
}
}

func TestPrivilegedProxy(t *testing.T) {
for _, test := range proxyTests {
privilegedToken := "90d64460d14870c08c81352a05dedd3465940a7c"
// Create the external server that the proxy will send the request to.
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != test.RequestPath {
t.Errorf("Server expected path %s but instead received path %s\n", test.RequestPath, r.URL.Path)
} else if r.Method != test.RequestMethod {
t.Errorf("Server expected method %s but instead received method %s\n", test.RequestMethod, r.Method)
} else {
w.WriteHeader(test.ResponseCode)
fmt.Fprintln(w, test.Response)
}
// Check that we are using the privileged token
// This line here is why we can't use the generic CreateExternalServer.
// Could add a token parameter. TODO
headerAuth := r.Header.Get("Authorization")
if headerAuth == "Basic "+privilegedToken {
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
}
}))
// Create the external server that will act as the UAA server to get the privileged token from.
testUAAServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() != "/oauth/token" {
t.Errorf("authenticate client request URL = %q; want %q", r.URL, "/token")
}

if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want {
t.Errorf("Content-Type header = %q; want %q", got, want)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
r.Body.Close()
}
if err != nil {
t.Errorf("failed reading request body: %s.", err)
}
if string(body) != "client_id="+MockCompleteEnvVars.ClientID+"&grant_type=client_credentials&scope=scim.read" {
t.Errorf("payload = %q; want %q", string(body), "client_id="+MockCompleteEnvVars.ClientID+"&grant_type=client_credentials&scope=scim.read")
}
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
// Write the privileged token so that it can be used.
w.Write([]byte("access_token=" + privilegedToken + "&token_type=bearer"))
}))
// We can only get this after the server has started.
test.EnvVars.UAAURL = testUAAServer.URL
// Construct full url for the proxy.
fullURL := fmt.Sprintf("%s%s", testServer.URL, test.RequestPath)
c := &controllers.SecureContext{Context: &controllers.Context{}}
response, request, _ := PrepareExternalServerCall(t, c, testServer, fullURL, test)
c.PrivilegedProxy(response, request, fullURL)
VerifyExternalCallResponse(t, response, &test)

testUAAServer.Close()
testServer.Close()
}
}
86 changes: 0 additions & 86 deletions controllers/uaa.go
@@ -1,12 +1,8 @@
package controllers

import (
"encoding/json"
"fmt"
"github.com/gocraft/web"
"io/ioutil"
"net/http"
"net/url"
)

// UAAContext stores the session info and access token per user.
Expand All @@ -21,89 +17,7 @@ func (c *UAAContext) uaaProxy(rw web.ResponseWriter, req *web.Request, uaaEndpoi
c.Proxy(rw, req.Request, reqURL, c.GenericResponseHandler)
}

// uaaPrivilegedProxy prepares the final URL to pass through the proxy with elevated privileges.
func (c *UAAContext) uaaPrivilegedProxy(rw web.ResponseWriter, req *http.Request, uaaEndpoint string) {
reqURL := fmt.Sprintf("%s%s", c.Settings.UaaURL, uaaEndpoint)
c.PrivilegedProxy(rw, req, reqURL)
}

// UserInfo returns the UAA_API/userinfo information for the logged in user.
func (c *UAAContext) UserInfo(rw web.ResponseWriter, req *web.Request) {
c.uaaProxy(rw, req, "/userinfo")
}

/*
By only looking for particular attributes, the response comes faster.
Attributes to return:
- id
- meta
- version (not helpful)
- created
- lastModified
- userName
- name
- familyName
- givenName
- emails
- groups (be careful, shows user scopes/ permissions)
*/

// QueryUser returns select data (id and userName) about all users.
// Additional information can be asked for by passing it view query string.
// Also, specific user(s) can be looked for by using the filter attribute.
// (eg: "?filter=id eq 'the-id'") spaces included
// More info about these filters can be found here:
// https://github.com/cloudfoundry/uaa/blob/master/docs/UAA-APIs.rst#query-for-information-get-users
// This function converts a POST request to a GET Request to prevent this:
// http://stackoverflow.com/questions/17303940/security-sending-email-address-in-a-url-parameter
// The payload of the POST request will be the filters to append to GET request.
// Basic format of payload
// {
// filter01: value01,
// filter02, value02,
// ..
// filterN, valueN
// }
func (c *UAAContext) QueryUser(rw web.ResponseWriter, req *web.Request) {
if req.Body == nil {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"status\": \"error\", \"message\": \"empty request body\"}")
return
}
// Read JSON body of filters
body, err := ioutil.ReadAll(req.Body)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"status\": \"error\", \"message\": \"unable to read body\"}")
return
}
defer req.Body.Close()
// TODO check error.
var filters map[string]string
json.Unmarshal(body, &filters)

// Make sure we never return all the results by default by requiring some filter(s).
numOfFilters := len(filters)
if numOfFilters < 1 {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"status\": \"error\", \"message\": \"not enough filters\"}")
return
}

// Create basic query
query := "/Users?attributes=id&attributes=userName&filter="
filtersAdded := 0
for filter, value := range filters {
// TODO make sure there's no wildcard value.
// TODO support other operators besides eq.
query += url.QueryEscape(filter + " eq '" + value + "'")
filtersAdded++
if filtersAdded != numOfFilters {
// TODO support more than just "and"
query += url.QueryEscape(" and ")
}
}
// Transform the POST request to a GET request by creating a new one.
request, _ := http.NewRequest("GET", query, nil)
c.uaaPrivilegedProxy(rw, request, query)
}
56 changes: 0 additions & 56 deletions controllers/uaa_test.go
Expand Up @@ -5,9 +5,7 @@ import (
. "github.com/18F/cg-deck/helpers/testhelpers"

"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)

Expand Down Expand Up @@ -96,57 +94,3 @@ var queryUsersTests = []BasicProxyTest{
ResponseCode: http.StatusOK,
},
}

func TestQueryUsers(t *testing.T) {
for _, test := range queryUsersTests {
privilegedToken := "90d64460d14870c08c81352a05dedd3465940a7c"
// Create the external server that will act as the UAA server to get the privileged token from and the same UAA server to return the query.
testUAAServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/oauth/token" {

if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want {
t.Errorf("Content-Type header = %q; want %q", got, want)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
r.Body.Close()
}
if err != nil {
t.Errorf("failed reading request body: %s.", err)
}
if string(body) != "client_id="+MockCompleteEnvVars.ClientID+"&grant_type=client_credentials&scope=scim.read" {
t.Errorf("payload = %q; want %q", string(body), "client_id="+MockCompleteEnvVars.ClientID+"&grant_type=client_credentials&scope=scim.read")
}
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
// Write the privileged token so that it can be used.
w.Write([]byte("access_token=" + privilegedToken + "&token_type=bearer"))
} else if r.URL.Path == test.ExpectedPath {
if r.Method != "GET" {
t.Errorf("Tests name: (%s) Server expected method %s but instead received method %s\n", test.TestName, "GET", r.Method)
} else {
w.WriteHeader(test.ResponseCode)
fmt.Fprintln(w, test.Response)
}
// Check that we are using the privileged token
// This line here is why we can't use the generic CreateExternalServer.
// Could add a token parameter. TODO
headerAuth := r.Header.Get("Authorization")
if headerAuth == "Basic "+privilegedToken {
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
}
} else {
t.Errorf("Unknown path. Got (%s) wanted (%s)\n", r.URL.Path, test.RequestPath)
}
}))
// We can only get this after the server has started.
test.EnvVars.UAAURL = testUAAServer.URL
// Construct full url for the proxy.
fullURL := fmt.Sprintf("%s%s", testUAAServer.URL, test.RequestPath)
c := &controllers.UAAContext{SecureContext: &controllers.SecureContext{Context: &controllers.Context{}}}
response, request, router := PrepareExternalServerCall(t, c.SecureContext, testUAAServer, fullURL, test)
router.ServeHTTP(response, request)
VerifyExternalCallResponse(t, response, &test)
testUAAServer.Close()

}
}

0 comments on commit cba601c

Please sign in to comment.