loginsrv

Unnamed repository; edit this file 'description' to name the repository.
git clone git@jamesshield.xyz:repos/loginsrv.git
Log | Files | Refs | README | LICENSE

commit d3d9047320034a21adb828ae29b274bfea4ac6ff
parent a7fc1f05ef3e7a423a8a164192b616b4ac9716ea
Author: Sebastian Mancke <s.mancke@tarent.de>
Date:   Sat, 19 Nov 2016 21:21:45 +0100

added test coverage for osiam parts

Diffstat:
Mlogin/handler.go | 16++++++++--------
Mosiam/backend.go | 10+++++-----
Aosiam/backend_test.go | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Mosiam/client.go | 12++++++++----
Aosiam/client_test.go | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mosiam/setup.go | 8+++-----
Mosiam/token.go | 4----
Aosiam/token_test.go | 42++++++++++++++++++++++++++++++++++++++++++
8 files changed, 264 insertions(+), 26 deletions(-)

diff --git a/login/handler.go b/login/handler.go @@ -11,9 +11,9 @@ import ( "strings" ) -const ContentTypeHtml = "text/html; charset=utf-8" -const ContentTypeJWT = "application/jwt" -const ContentTypePlain = "text/plain" +const contentTypeHtml = "text/html; charset=utf-8" +const contentTypeJWT = "application/jwt" +const contentTypePlain = "text/plain" type Handler struct { backends []Backend @@ -127,7 +127,7 @@ func (h *Handler) respondAuthenticated(w http.ResponseWriter, r *http.Request, u return } - w.Header().Set("Content-Type", ContentTypeJWT) + w.Header().Set("Content-Type", contentTypeJWT) w.WriteHeader(200) fmt.Fprintf(w, "%s\n", token) } @@ -139,7 +139,7 @@ func (h *Handler) createToken(userInfo UserInfo) (string, error) { func (h *Handler) respondError(w http.ResponseWriter, r *http.Request) { if wantHtml(r) { - w.Header().Set("Content-Type", ContentTypeHtml) + w.Header().Set("Content-Type", contentTypeHtml) w.WriteHeader(500) username, _, _ := getCredentials(r) writeLoginForm(w, @@ -151,7 +151,7 @@ func (h *Handler) respondError(w http.ResponseWriter, r *http.Request) { }) return } - w.Header().Set("Content-Type", ContentTypePlain) + w.Header().Set("Content-Type", contentTypePlain) w.WriteHeader(500) fmt.Fprintf(w, "Internal Server Error") } @@ -163,7 +163,7 @@ func (h *Handler) respondBadRequest(w http.ResponseWriter, r *http.Request) { func (h *Handler) respondAuthFailure(w http.ResponseWriter, r *http.Request) { if wantHtml(r) { - w.Header().Set("Content-Type", ContentTypeHtml) + w.Header().Set("Content-Type", contentTypeHtml) w.WriteHeader(403) username, _, _ := getCredentials(r) writeLoginForm(w, @@ -176,7 +176,7 @@ func (h *Handler) respondAuthFailure(w http.ResponseWriter, r *http.Request) { }) return } - w.Header().Set("Content-Type", ContentTypePlain) + w.Header().Set("Content-Type", contentTypePlain) w.WriteHeader(403) fmt.Fprintf(w, "Wrong credentials") } diff --git a/osiam/backend.go b/osiam/backend.go @@ -7,12 +7,12 @@ import ( "net/url" ) -type OsiamBackend struct { +type Backend struct { client *Client } -// NewOsiamBackend creates a new OSIAM Backend and verifies the parameters. -func NewOsiamBackend(endpoint, clientId, clientSecret string) (*OsiamBackend, error) { +// NewBackend creates a new OSIAM Backend and verifies the parameters. +func NewBackend(endpoint, clientId, clientSecret string) (*Backend, error) { if _, err := url.Parse(endpoint); err != nil { return nil, fmt.Errorf("osiam endpoint has to be a valid url: %v: %v", endpoint, err) } @@ -24,12 +24,12 @@ func NewOsiamBackend(endpoint, clientId, clientSecret string) (*OsiamBackend, er return nil, errors.New("No osiam clientSecret provided.") } client := NewClient(endpoint, clientId, clientSecret) - return &OsiamBackend{ + return &Backend{ client: client, }, nil } -func (b *OsiamBackend) Authenticate(username, password string) (bool, login.UserInfo, error) { +func (b *Backend) Authenticate(username, password string) (bool, login.UserInfo, error) { authenticated, _, err := b.client.GetTokenByPassword(username, password) if !authenticated || err != nil { return authenticated, login.UserInfo{}, err diff --git a/osiam/backend_test.go b/osiam/backend_test.go @@ -0,0 +1,53 @@ +package osiam + +import ( + "github.com/stretchr/testify/assert" + "github.com/tarent/loginsrv/login" + "net/http" + "net/http/httptest" + "testing" +) + +func TestBackend_Authenticate(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(osiamMockHandler)) + defer server.Close() + + // positive case + backend, err := NewBackend(server.URL, "example-client", "secret") + assert.NoError(t, err) + authenticated, userInfo, err := backend.Authenticate("admin", "koala") + + assert.NoError(t, err) + assert.True(t, authenticated) + assert.Equal(t, + login.UserInfo{ + Username: "admin", + }, + userInfo) + + // wrong client credentials + backend, err = NewBackend(server.URL, "example-client", "XXX") + assert.NoError(t, err) + authenticated, userInfo, err = backend.Authenticate("admin", "koala") + assert.Error(t, err) + assert.False(t, authenticated) + + // wrong user credentials + backend, err = NewBackend(server.URL, "example-client", "secret") + assert.NoError(t, err) + authenticated, userInfo, err = backend.Authenticate("admin", "XXX") + assert.NoError(t, err) + assert.False(t, authenticated) + +} + +func TestBackend_AuthenticateErrorCases(t *testing.T) { + _, err := NewBackend("://", "example-client", "secret") + assert.Error(t, err) + + _, err = NewBackend("http://example.com", "", "secret") + assert.Error(t, err) + + _, err = NewBackend("http://example.com", "example-client", "") + assert.Error(t, err) +} diff --git a/osiam/client.go b/osiam/client.go @@ -45,15 +45,19 @@ func (c *Client) GetTokenByPassword(username, password string, scopes ...string) return false, nil, err } - if !isJson(res.Header.Get("Content-Type")) { - return false, nil, fmt.Errorf("Expected a token in json format, but got Content-Type: %v", res.Header.Get("Content-Type")) - } - body, err := ioutil.ReadAll(res.Body) if err != nil { return false, nil, err } + if !isJson(res.Header.Get("Content-Type")) { + bodyStart := string(body) + if len(bodyStart) > 50 { + bodyStart = bodyStart[0:50] + } + return false, nil, fmt.Errorf("Expected a token in json format, but got Content-Type: %q and message starting with %q", res.Header.Get("Content-Type"), bodyStart) + } + if res.StatusCode == 200 { token = &Token{} err = json.Unmarshal(body, token) diff --git a/osiam/client_test.go b/osiam/client_test.go @@ -0,0 +1,145 @@ +package osiam + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func TestClient_GetTokenByPassword(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(osiamMockHandler)) + defer server.Close() + + client := NewClient(server.URL, "example-client", "secret") + authenticated, token, err := client.GetTokenByPassword("admin", "koala") + + assert.NoError(t, err) + assert.True(t, authenticated) + assert.Equal(t, + &Token{ + Userid: "84f6cffa-4505-48ec-a851-424160892283", + ExpiresIn: 28493, + ExpiresAt: Timestamp{time.Unix(1479600891424, 0)}, + RefreshToken: "15b22304-f838-48c2-9c40-18bf285060a6", + ClientId: "example-client", + RefreshTokenExpiresAt: Timestamp{time.Unix(1479575304893, 0)}, + UserName: "admin", + TokenType: "bearer", + AccessToken: "59f39ef8-1dc3-4c0d-8dea-c9597ef0a8ef", + Scope: "ME", + }, + token) + assert.True(t, len(token.RefreshToken) > 0) + assert.True(t, token.ExpiresIn > 0) +} + +func TestClient_GetTokenByPasswordErrorCases(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(osiamMockHandler)) + defer server.Close() + + // wrong credentials + client := NewClient(server.URL, "example-client", "secret") + authenticated, _, err := client.GetTokenByPassword("admin", "XXX") + assert.NoError(t, err) + assert.False(t, authenticated) + + // wrong url -> 404 + client = NewClient(server.URL+"/Foo", "example-client", "secret") + _, _, err = client.GetTokenByPassword("admin", "koala") + assert.Error(t, err) + + // wrong client secret + client = NewClient(server.URL, "example-client", "XXX") + _, _, err = client.GetTokenByPassword("admin", "koala") + assert.Error(t, err) + + // invalid url + client = NewClient("://", "example-client", "secret") + _, _, err = client.GetTokenByPassword("admin", "koala") + assert.Error(t, err) + +} + +func TestClient_GetTokenByPasswordInvalidJson(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json;charset=UTF-8") + w.WriteHeader(200) + fmt.Fprintf(w, "{...") + return + })) + defer server.Close() + + client := NewClient(server.URL, "example-client", "secret") + _, _, err := client.GetTokenByPassword("admin", "koala") + assert.Error(t, err) +} + +func TestClient_GetTokenByPasswordUnknownError(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json;charset=UTF-8") + w.WriteHeader(201) + fmt.Fprintf(w, `{"error":"foo bar","error_description":"some message!"}`) + return + })) + defer server.Close() + + client := NewClient(server.URL, "example-client", "secret") + _, _, err := client.GetTokenByPassword("admin", "koala") + assert.Error(t, err) +} + +func TestClient_GetTokenByPasswordNoServer(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + })) + server.Close() + + client := NewClient(server.URL, "example-client", "secret") + _, _, err := client.GetTokenByPassword("admin", "koala") + assert.Error(t, err) +} + +func osiamMockHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(400) + fmt.Fprintf(w, `Method not supported`) + return + } + + if r.URL.Path != "/oauth/token" { + w.WriteHeader(404) + fmt.Fprintf(w, "Not found %q", r.URL.Path) + return + } + + w.Header().Set("Content-Type", "application/json;charset=UTF-8") + + if r.Header.Get("Authorization") != "Basic ZXhhbXBsZS1jbGllbnQ6c2VjcmV0" { + w.WriteHeader(401) + fmt.Fprintf(w, `{"timestamp":1479572095876,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/oauth/token"}`) + return + } + b, _ := ioutil.ReadAll(r.Body) + + if string(b) != "grant_type=password&username=admin&password=koala&scope=ME" { + w.WriteHeader(400) + fmt.Fprintf(w, `{"error":"invalid_grant","error_description":"some message!"}`) + return + } + + w.WriteHeader(200) + fmt.Fprintf(w, `{ + "user_id" : "84f6cffa-4505-48ec-a851-424160892283", + "expires_in" : 28493, + "expires_at" : 1479600891424, + "refresh_token" : "15b22304-f838-48c2-9c40-18bf285060a6", + "client_id" : "example-client", + "refresh_token_expires_at" : 1479575304893, + "user_name" : "admin", + "token_type" : "bearer", + "access_token" : "59f39ef8-1dc3-4c0d-8dea-c9597ef0a8ef", + "scope" : "ME"}`) +} diff --git a/osiam/setup.go b/osiam/setup.go @@ -11,9 +11,7 @@ func init() { &login.ProviderDescription{ Name: OsiamProviderName, }, - OsiamBackendFactory) -} - -func OsiamBackendFactory(config map[string]string) (login.Backend, error) { - return NewOsiamBackend(config["endpoint"], config["clientId"], config["clientSecret"]) + func(config map[string]string) (login.Backend, error) { + return NewBackend(config["endpoint"], config["clientId"], config["clientSecret"]) + }) } diff --git a/osiam/token.go b/osiam/token.go @@ -41,7 +41,3 @@ func (timestamp *Timestamp) MarshalJSON() ([]byte, error) { } var nilTime = (time.Time{}).UnixNano() - -func (timestamp *Timestamp) IsSet() bool { - return timestamp.T.UnixNano() != nilTime -} diff --git a/osiam/token_test.go b/osiam/token_test.go @@ -0,0 +1,42 @@ +package osiam + +import ( + "encoding/json" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +var testToken = &Token{ + Userid: "84f6cffa-4505-48ec-a851-424160892283", + ExpiresIn: 28493, + ExpiresAt: Timestamp{time.Unix(1479600891424, 0)}, + RefreshToken: "15b22304-f838-48c2-9c40-18bf285060a6", + ClientId: "example-client", + RefreshTokenExpiresAt: Timestamp{time.Unix(1479575304893, 0)}, + UserName: "admin", + TokenType: "bearer", + AccessToken: "59f39ef8-1dc3-4c0d-8dea-c9597ef0a8ef", + Scope: "ME", +} + +var testTokenString = `{ + "user_id" : "84f6cffa-4505-48ec-a851-424160892283", + "expires_in" : 28493, + "expires_at" : 1479600891424, + "refresh_token" : "15b22304-f838-48c2-9c40-18bf285060a6", + "client_id" : "example-client", + "refresh_token_expires_at" : 1479575304893, + "user_name" : "admin", + "token_type" : "bearer", + "access_token" : "59f39ef8-1dc3-4c0d-8dea-c9597ef0a8ef", + "scope" : "ME"}` + +func TestClient_TokenMarshaling(t *testing.T) { + tk := &Token{} + json.Unmarshal([]byte(testTokenString), tk) + assert.Equal(t, testToken, tk) + + _, err := json.Marshal(tk) + assert.NoError(t, err) +}