Commit 6b5eeda7 authored by Gurvinder Singh's avatar Gurvinder Singh
Browse files

added support for passing email and mas groups if enabled

parent 49bb0ebb
FROM alpine:3.5 FROM alpine:3.8
RUN apk update && apk add ca-certificates RUN apk update && apk add ca-certificates
...@@ -7,4 +7,4 @@ COPY ./jwt-tokenissuer /bin/ ...@@ -7,4 +7,4 @@ COPY ./jwt-tokenissuer /bin/
USER nobody USER nobody
EXPOSE 8888 EXPOSE 8888
CMD ["/bin/jwt-tokenissuer", "-c", "/config/jwt"] CMD ["/bin/jwt-tokenissuer", "-c", "/config/jwt.json"]
...@@ -10,3 +10,4 @@ import: ...@@ -10,3 +10,4 @@ import:
- package: scm.uninett.no/laas/laasctl-auth - package: scm.uninett.no/laas/laasctl-auth
- package: github.com/stretchr/testify/assert - package: github.com/stretchr/testify/assert
- package: golang.org/x/net/html/charset - package: golang.org/x/net/html/charset
- package: github.com/davecgh/go-spew/spew
...@@ -2,6 +2,8 @@ package main ...@@ -2,6 +2,8 @@ package main
import ( import (
"crypto/rsa" "crypto/rsa"
"encoding/base64"
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
...@@ -48,6 +50,12 @@ func (jwm *JWTMiddleware) JWTTokenHandler() http.Handler { ...@@ -48,6 +50,12 @@ func (jwm *JWTMiddleware) JWTTokenHandler() http.Handler {
principals = rawGroups.([]string) principals = rawGroups.([]string)
} }
} }
// Check if we need to get groups from MAS
if conf.GetStringValue("engine.mas.groups_endpoint") != "" {
principals = append(principals, jwm.getMASgroups(r.Header.Get("X-Dataporten-Userid-Sec"))...)
}
// Get Client ID from request headers // Get Client ID from request headers
clientID, clientIDFound := r.Header["X-Dataporten-Clientid"] clientID, clientIDFound := r.Header["X-Dataporten-Clientid"]
if !clientIDFound { if !clientIDFound {
...@@ -55,7 +63,11 @@ func (jwm *JWTMiddleware) JWTTokenHandler() http.Handler { ...@@ -55,7 +63,11 @@ func (jwm *JWTMiddleware) JWTTokenHandler() http.Handler {
return return
} }
token := jwm.getJWTToken(w, r, user, principals, clientID[0]) // Get Email
email := jwm.getEmail(r.Header.Get("X-Dataporten-Token"))
// Get the JWT token with all the required information
token := jwm.getJWTToken(w, r, user, principals, clientID[0], email)
if token == nil { if token == nil {
return return
} }
...@@ -76,7 +88,8 @@ func (jwm *JWTMiddleware) getJWTToken( ...@@ -76,7 +88,8 @@ func (jwm *JWTMiddleware) getJWTToken(
r *http.Request, r *http.Request,
user string, user string,
principals []string, principals []string,
aud string) []byte { aud string,
email string) []byte {
if user == "" || aud == "" { if user == "" || aud == "" {
auth.ReturnError(w, r, "User or Audience is empty", http.StatusBadRequest) auth.ReturnError(w, r, "User or Audience is empty", http.StatusBadRequest)
log.Error("User or audience is empty") log.Error("User or audience is empty")
...@@ -98,6 +111,9 @@ func (jwm *JWTMiddleware) getJWTToken( ...@@ -98,6 +111,9 @@ func (jwm *JWTMiddleware) getJWTToken(
claims.Set("principals", principals) claims.Set("principals", principals)
claims.Set("acr_values", r.Header.Get("X-Dataporten-Acr")) claims.Set("acr_values", r.Header.Get("X-Dataporten-Acr"))
claims.Set("userid-sec", r.Header.Get("X-Dataporten-Userid-Sec")) claims.Set("userid-sec", r.Header.Get("X-Dataporten-Userid-Sec"))
if email != "" {
claims.Set("email", email)
}
claims.SetIssuer(conf.GetStringValue("engine.issuer_url")) claims.SetIssuer(conf.GetStringValue("engine.issuer_url"))
claims.SetExpiration(now.Add(time.Duration(lifeTime) * time.Second)) claims.SetExpiration(now.Add(time.Duration(lifeTime) * time.Second))
...@@ -110,3 +126,60 @@ func (jwm *JWTMiddleware) getJWTToken( ...@@ -110,3 +126,60 @@ func (jwm *JWTMiddleware) getJWTToken(
} }
return token return token
} }
// Get email from UserInfo endpoint using Access Token given by GateKeeper
func (jwm *JWTMiddleware) getEmail(token string) string {
dataReq, err := http.NewRequest("GET", conf.GetStringValue("engine.userinfo_endpoint"), nil)
if err != nil {
log.Warn("Failed in creating request to fecth data from DataPorten")
return ""
}
dataReq.Header["Authorization"] = []string{"Bearer " + token}
data := getReqData(dataReq)
var userInfo interface{}
err = json.Unmarshal(data, &userInfo)
if err != nil {
log.Warn("Failed in parsing email data from ", err)
return ""
}
user := userInfo.(map[string]interface{})
if _, ok := user["email"]; ok {
return user["email"].(string)
}
log.Warn("Failed in parsing userInfo data")
return ""
}
// Get MAS groups from MAS end point which provides groups in similar
// format as Dataporten
func (jwm *JWTMiddleware) getMASgroups(feideID string) []string {
dataReq, err := http.NewRequest("GET", conf.GetStringValue("engine.mas.groups_endpoint"), nil)
if err != nil {
log.Warn("Failed in creating request to fecth data from MAS")
return nil
}
// Set Feide ID as this tells to MAS Api which user data to return
dataReq.Header["X-Dataporten-Userid-Sec"] = []string{feideID}
authVal := conf.GetStringValue("engine.mas.basic_auth.mas_creds")
basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(authVal))
dataReq.Header["Authorization"] = []string{basicAuth}
data := getReqData(dataReq)
var groupsData []interface{}
var groups []string
err = json.Unmarshal(data, &groupsData)
if err != nil {
log.Debug("Failed in parsing groups data from MAS")
return nil
}
for _, group := range groupsData {
group := group.(map[string]interface{})
if _, ok := group["id"]; ok {
groups = append(groups, group["id"].(string))
}
}
return groups
}
...@@ -29,14 +29,14 @@ func TestGetJWTToken(t *testing.T) { ...@@ -29,14 +29,14 @@ func TestGetJWTToken(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "http://example.com/foo", nil) r := httptest.NewRequest("GET", "http://example.com/foo", nil)
token := jwm.getJWTToken(w, r, "dummy", []string{"dummyPp"}, "dummyapp") token := jwm.getJWTToken(w, r, "dummy", []string{"dummyPp"}, "dummyapp", "test@test.com")
assert.NotNil(t, token) assert.NotNil(t, token)
token = jwm.getJWTToken(w, r, "dummy", nil, "dummyapp") token = jwm.getJWTToken(w, r, "dummy", nil, "dummyapp", "test@test.com")
assert.NotNil(t, token) assert.NotNil(t, token)
token = jwm.getJWTToken(w, r, "dummy", nil, "") token = jwm.getJWTToken(w, r, "dummy", nil, "", "test@test.com")
assert.Nil(t, token) assert.Nil(t, token)
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
token = jwm.getJWTToken(w, r, "", nil, "") token = jwm.getJWTToken(w, r, "", nil, "", "test@test.com")
assert.Nil(t, token) assert.Nil(t, token)
assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
} }
......
package main
import (
"io/ioutil"
"net/http"
log "github.com/Sirupsen/logrus"
)
func getReqData(req *http.Request) []byte {
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Warn("Failed in fetching data from ", req.URL, err)
return nil
}
if resp != nil && resp.StatusCode != 200 {
log.Warn("Failed in fetching data from ", req.URL, "error code ", resp.StatusCode)
return nil
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
log.Warn("Failed in reading body from ", req.URL)
return nil
}
return body
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment