Skip to content
Snippets Groups Projects
Commit 67cd281c authored by Rohith's avatar Rohith
Browse files

Merge pull request #3 from gambol99/rj/wip

fixes
parents 13880bee b859a1b6
Branches
Tags v0.0.2
No related merge requests found
......@@ -32,22 +32,40 @@ import (
// c) proxyHandler is responsible for handling the reverse proxy to the upstream endpoint
//
/*
// loggingHandler is logging middleware
func (r *KeycloakProxy) loggingHandler() gin.HandlerFunc {
const (
authRequired = "AUTH_REQUIRED"
)
// entrypointHandler checks to see if the request requires authentication
func (r *KeycloakProxy) entrypointHandler() gin.HandlerFunc {
return func(cx *gin.Context) {
req, err := httputil.DumpRequest(cx.Request, true)
if err == nil {
glog.V(10).Infof("%s", req)
glog.V(10).Infof("entering the entrypoint handler, uri: %s", cx.Request.RequestURI)
// check if authentication is required
for _, resource := range r.config.Resources {
if strings.HasPrefix(cx.Request.RequestURI, resource.URL) {
if containedIn(cx.Request.Method, resource.Methods) {
cx.Set(authRequired, true)
} else if containedIn("ANY", resource.Methods) {
cx.Set(authRequired, true)
}
break
}
}
}
}
*/
// authenticationHandler is responsible for verifying the access token
func (r *KeycloakProxy) authenticationHandler(cx *gin.Context) {
func (r *KeycloakProxy) authenticationHandler() gin.HandlerFunc {
return func(cx *gin.Context) {
glog.V(10).Infof("entering the authentication handler, uri: %s", cx.Request.RequestURI)
// step: is authentication required on this
if _, found := cx.Get(authRequired); !found {
return
}
// step: extract the token if there is one
// a) if there is no token, we check for session state and if so, we try to refresh the token
// b) there is no token or session state, we simple redirect to keycloak
......@@ -102,9 +120,16 @@ func (r *KeycloakProxy) authenticationHandler(cx *gin.Context) {
}
}
}
}
// admissionHandler is responsible checking the access token against the protected resource
func (r *KeycloakProxy) admissionHandler(cx *gin.Context) {
func (r *KeycloakProxy) admissionHandler() gin.HandlerFunc {
return func(cx *gin.Context) {
// step: is authentication required on this
if _, found := cx.Get(authRequired); !found {
return
}
// step: grab the identity from the context
userContext, found := cx.Get(userContextName)
if !found {
......@@ -134,6 +159,7 @@ func (r *KeycloakProxy) admissionHandler(cx *gin.Context) {
}
}
}
}
// proxyHandler is responsible to proxy the requests on to the upstream endpoint
func (r *KeycloakProxy) proxyHandler(cx *gin.Context) {
......
......@@ -19,7 +19,6 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
"sync"
"github.com/gin-gonic/gin"
......@@ -40,7 +39,8 @@ func NewProxy(cfg *Config) (*KeycloakProxy, error) {
}
// step: initialize the openid client
client, clientCfg, err := initializeOpenID(cfg.DiscoveryURL, cfg.ClientID, cfg.Secret, cfg.Scopes)
client, clientCfg, err := initializeOpenID(cfg.DiscoveryURL,
cfg.ClientID, cfg.Secret, cfg.RedirectionURL, cfg.Scopes)
if err != nil {
return nil, err
}
......@@ -59,11 +59,9 @@ func NewProxy(cfg *Config) (*KeycloakProxy, error) {
service.router = gin.Default()
for _, resource := range cfg.Resources {
glog.Infof("protecting resource: %s", resource)
for _, method := range resource.Methods {
service.router.Handle(strings.ToUpper(method), resource.URL, service.authenticationHandler, service.admissionHandler)
}
glog.Infof("protecting resources under: %s", resource)
}
service.router.Use(service.entrypointHandler(), service.authenticationHandler(), service.admissionHandler())
// step: add the oauth handlers and health
service.router.GET(authorizationURL, service.authorizationHandler)
......
......@@ -105,7 +105,7 @@ func (r *KeycloakProxy) getUserContext(token jose.JWT) (*UserContext, error) {
}
// step: get the preferred name
preferredName, _, err := claims.StringClaim("preferred_name")
preferredName, _, err := claims.StringClaim("preferred_username")
if err != nil {
glog.Warningf("unable to extract the preferred name from the token claims, reason: %s", err)
}
......
......@@ -18,12 +18,62 @@ package main
import (
"testing"
"time"
"reflect"
"github.com/stretchr/testify/assert"
"github.com/gambol99/go-oidc/jose"
)
func TestGetUserContext(t *testing.T) {
proxy := newFakeKeycloakProxy(t)
testToken, err := jose.NewJWT(
jose.JOSEHeader{
"alg": "RS256",
},
jose.Claims{
"jti": "4ee75b8e-3ee6-4382-92d4-3390b4b4937b",
//"exp": "1450372969",
"nbf": 0,
"iat": "1450372669",
"iss": "https://keycloak.example.com/auth/realms/commons",
"aud": "test",
"sub": "1e11e539-8256-4b3b-bda8-cc0d56cddb48",
"typ": "Bearer",
"azp": "clientid",
"session_state": "98f4c3d2-1b8c-4932-b8c4-92ec0ea7e195",
"client_session": "f0105893-369a-46bc-9661-ad8c747b1a69",
"resource_access": map[string]interface{}{
"openvpn": map[string]interface{}{
"roles": []string{
"dev-vpn",
},
},
},
"email": "gambol99@gmail.com",
"name": "Rohith Jayawardene",
"family_name": "Jayawardene",
"preferred_username": "rjayawardene",
"given_name": "Rohith",
})
if assert.NoError(t, err, "should not have recieved an error parsing the token") {
t.Failed()
}
if !assert.NotNil(t, testToken, "should not have got nil from token") {
t.FailNow()
}
context, err := proxy.getUserContext(testToken)
assert.NoError(t, err)
assert.NotNil(t, context)
assert.Equal(t, "1e11e539-8256-4b3b-bda8-cc0d56cddb48", context.id)
assert.Equal(t, "gambol99@gmail.com", context.email)
assert.Equal(t, "rjayawardene", context.preferredName)
roles := []string{"openvpn:dev-vpn"}
if !reflect.DeepEqual(context.roles, roles) {
t.Errorf("the claims are not the same, %v <-> %v", context.roles, roles)
}
}
func TestEncodeState(t *testing.T) {
......
......@@ -41,7 +41,7 @@ import (
)
var (
httpMethodRegex = regexp.MustCompile("^(GET|POST|DELETE|PATCH|HEAD|PUT|TRACE|CONNECT)$")
httpMethodRegex = regexp.MustCompile("^(ANY|GET|POST|DELETE|PATCH|HEAD|PUT|TRACE|CONNECT)$")
)
// encryptDataBlock encrypts the plaintext string with the key
......@@ -88,7 +88,7 @@ func decryptDataBlock(cipherText, key []byte) ([]byte, error) {
// initializeOpenID initializes the openID configuration, note: the redirection url is deliberately left blank
// in order to retrieve it from the host header on request
func initializeOpenID(discoveryURL, clientID, clientSecret string, scopes []string) (*oidc.Client, oidc.ClientConfig, error) {
func initializeOpenID(discoveryURL, clientID, clientSecret, redirectURL string, scopes []string) (*oidc.Client, oidc.ClientConfig, error) {
var err error
var providerConfig oidc.ProviderConfig
......@@ -116,7 +116,7 @@ func initializeOpenID(discoveryURL, clientID, clientSecret string, scopes []stri
ID: clientID,
Secret: clientSecret,
},
RedirectURL: "http://127.0.0.1:3000/oauth/callback",
RedirectURL: fmt.Sprintf("%s/oauth/callback", redirectURL),
Scope: append(scopes, oidc.DefaultScope...),
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment