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 a769eff0a2f2bee9f82eac0adc716d702ae29c51
parent 4276628b951927db41e02f046e853e50b3799e38
Author: Sebastian Mancke <s.mancke@tarent.de>
Date:   Mon, 15 May 2017 11:56:24 +0200

fixed most golint findings

Diffstat:
Mcaddy/handler.go | 3++-
Mcaddy/setup.go | 8+++++---
Mcaddy/setup_test.go | 23+++++++++++------------
Mhtpasswd/auth.go | 3+++
Mhtpasswd/backend.go | 5++++-
Mlogin/backend.go | 1+
Mlogin/config.go | 21++++++++++++---------
Mlogin/config_test.go | 12++++++------
Mlogin/handler.go | 24+++++++++++++-----------
Mlogin/handler_test.go | 20++++++++++----------
Mlogin/login_form.go | 2+-
Mlogin/provider_description.go | 1+
Mlogin/simple_backend.go | 3+++
Mmodel/user_info.go | 6++++--
Moauth2/github.go | 5+++--
Moauth2/github_test.go | 6+++---
Moauth2/manager.go | 19+++++++++++--------
Moauth2/manager_test.go | 8++++----
Moauth2/oauth.go | 10+++++-----
Moauth2/oauth_test.go | 6+++---
Moauth2/provider.go | 4++--
Mosiam/backend.go | 12+++++++-----
Mosiam/client.go | 16+++++++++-------
Mosiam/client_test.go | 2+-
Mosiam/error.go | 4++++
Mosiam/setup.go | 1+
Mosiam/token.go | 5++++-
Mosiam/token_test.go | 2+-
28 files changed, 134 insertions(+), 98 deletions(-)

diff --git a/caddy/handler.go b/caddy/handler.go @@ -3,17 +3,18 @@ package caddy import ( "github.com/mholt/caddy/caddyhttp/httpserver" "github.com/tarent/loginsrv/login" - _ "github.com/tarent/loginsrv/osiam" "net/http" "strings" ) +// CaddyHandler is the loginsrv handler wrapper for caddy type CaddyHandler struct { next httpserver.Handler config *login.Config loginHandler *login.Handler } +// NewCaddyHandler create the handler func NewCaddyHandler(next httpserver.Handler, loginHandler *login.Handler, config *login.Config) *CaddyHandler { h := &CaddyHandler{ next: next, diff --git a/caddy/setup.go b/caddy/setup.go @@ -6,14 +6,16 @@ import ( "github.com/mholt/caddy" "github.com/mholt/caddy/caddyhttp/httpserver" "github.com/tarent/lib-compose/logging" - _ "github.com/tarent/loginsrv/htpasswd" "github.com/tarent/loginsrv/login" - _ "github.com/tarent/loginsrv/oauth2" - _ "github.com/tarent/loginsrv/osiam" "os" "path" "path/filepath" "strings" + + // Import all backends, packaged with the caddy plugin + _ "github.com/tarent/loginsrv/htpasswd" + _ "github.com/tarent/loginsrv/oauth2" + _ "github.com/tarent/loginsrv/osiam" ) func init() { diff --git a/caddy/setup_test.go b/caddy/setup_test.go @@ -29,10 +29,10 @@ func TestSetup(t *testing.T) { config: login.Config{ JwtSecret: "jwtsecret", JwtExpiry: 24 * time.Hour, - SuccessUrl: "/", + SuccessURL: "/", LoginPath: "/login", CookieName: "jwt_token", - CookieHttpOnly: true, + CookieHTTPOnly: true, Backends: login.Options{ "simple": map[string]string{ "bob": "secret", @@ -56,12 +56,12 @@ func TestSetup(t *testing.T) { config: login.Config{ JwtSecret: "jwtsecret", JwtExpiry: 42 * time.Hour, - SuccessUrl: "successurl", + SuccessURL: "successurl", LoginPath: "/foo/bar", CookieName: "cookiename", CookieDomain: "example.com", CookieExpiry: 23*time.Hour + 23*time.Minute, - CookieHttpOnly: false, + CookieHTTPOnly: false, Backends: login.Options{ "simple": map[string]string{ "bob": "secret", @@ -86,10 +86,10 @@ func TestSetup(t *testing.T) { config: login.Config{ JwtSecret: "jwtsecret", JwtExpiry: 24 * time.Hour, - SuccessUrl: "/", + SuccessURL: "/", LoginPath: "/context/login", CookieName: "cookiename", - CookieHttpOnly: true, + CookieHTTPOnly: true, Backends: login.Options{ "simple": map[string]string{ "bob": "secret", @@ -109,10 +109,10 @@ func TestSetup(t *testing.T) { config: login.Config{ JwtSecret: "jwtsecret", JwtExpiry: 24 * time.Hour, - SuccessUrl: "/", + SuccessURL: "/", LoginPath: "/login", CookieName: "cookiename", - CookieHttpOnly: true, + CookieHTTPOnly: true, Backends: login.Options{ "simple": map[string]string{ "bob": "secret", @@ -130,10 +130,10 @@ func TestSetup(t *testing.T) { config: login.Config{ JwtSecret: "jwtsecret", JwtExpiry: 24 * time.Hour, - SuccessUrl: "/", + SuccessURL: "/", LoginPath: "/login", CookieName: "jwt_token", - CookieHttpOnly: true, + CookieHTTPOnly: true, Backends: login.Options{ "simple": map[string]string{ "bob": "secret", @@ -155,9 +155,8 @@ func TestSetup(t *testing.T) { if test.shouldErr { Error(t, err, "test ") return - } else { - NoError(t, err) } + NoError(t, err) mids := httpserver.GetConfig(c).Middleware() if len(mids) == 0 { t.Errorf("no middlewares created in test #%v", j) diff --git a/htpasswd/auth.go b/htpasswd/auth.go @@ -14,11 +14,13 @@ import ( "strings" ) +// Auth is the htpassword authenticater type Auth struct { filename string userHash map[string]string } +// NewAuth creates an htpassword authenticater func NewAuth(filename string) (*Auth, error) { a := &Auth{ filename: filename, @@ -53,6 +55,7 @@ func (a *Auth) parse(filename string) error { return nil } +// Authenticate the user func (a *Auth) Authenticate(username, password string) (bool, error) { if hash, exist := a.userHash[username]; exist { h := []byte(hash) diff --git a/htpasswd/backend.go b/htpasswd/backend.go @@ -6,6 +6,7 @@ import ( "github.com/tarent/loginsrv/model" ) +// ProviderName const const ProviderName = "htpasswd" func init() { @@ -17,11 +18,12 @@ func init() { BackendFactory) } +// BackendFactory creates a htpasswd backend func BackendFactory(config map[string]string) (login.Backend, error) { if f, exist := config["file"]; exist { return NewBackend(f) } - return nil, errors.New(`missing parameter "file" for htpasswd provider.`) + return nil, errors.New(`missing parameter "file" for htpasswd provider`) } // Backend is a htpasswd based authentication backend. @@ -37,6 +39,7 @@ func NewBackend(filename string) (*Backend, error) { }, err } +// Authenticate the user func (sb *Backend) Authenticate(username, password string) (bool, model.UserInfo, error) { authenticated, err := sb.auth.Authenticate(username, password) if authenticated && err == nil { diff --git a/login/backend.go b/login/backend.go @@ -4,6 +4,7 @@ import ( "github.com/tarent/loginsrv/model" ) +// Backend is an loginsrv authentication extension type Backend interface { // Authenticate checks the username/password against the backend. // On success it returns true and a UserInfo object which has at least the username set. diff --git a/login/config.go b/login/config.go @@ -19,6 +19,7 @@ func init() { jwtDefaultSecret = randStringBytes(32) } +// DefaultConfig for the loginsrv handler func DefaultConfig() *Config { return &Config{ Host: "localhost", @@ -26,11 +27,11 @@ func DefaultConfig() *Config { LogLevel: "info", JwtSecret: jwtDefaultSecret, JwtExpiry: 24 * time.Hour, - SuccessUrl: "/", - LogoutUrl: "", + SuccessURL: "/", + LogoutURL: "", LoginPath: "/login", CookieName: "jwt_token", - CookieHttpOnly: true, + CookieHTTPOnly: true, Backends: Options{}, Oauth: Options{}, } @@ -38,6 +39,7 @@ func DefaultConfig() *Config { const envPrefix = "LOGINSRV_" +// Config for the loginsrv handler type Config struct { Host string Port string @@ -45,14 +47,14 @@ type Config struct { TextLogging bool JwtSecret string JwtExpiry time.Duration - SuccessUrl string - LogoutUrl string + SuccessURL string + LogoutURL string Template string LoginPath string CookieName string CookieExpiry time.Duration CookieDomain string - CookieHttpOnly bool + CookieHTTPOnly bool Backends Options Oauth Options } @@ -92,11 +94,11 @@ func (c *Config) ConfigureFlagSet(f *flag.FlagSet) { f.StringVar(&c.JwtSecret, "jwt-secret", "random key", "The secret to sign the jwt token") f.DurationVar(&c.JwtExpiry, "jwt-expiry", c.JwtExpiry, "The expiry duration for the jwt token, e.g. 2h or 3h30m") f.StringVar(&c.CookieName, "cookie-name", c.CookieName, "The name of the jwt cookie") - f.BoolVar(&c.CookieHttpOnly, "cookie-http-only", c.CookieHttpOnly, "Set the cookie with the http only flag") + f.BoolVar(&c.CookieHTTPOnly, "cookie-http-only", c.CookieHTTPOnly, "Set the cookie with the http only flag") f.DurationVar(&c.CookieExpiry, "cookie-expiry", c.CookieExpiry, "The expiry duration for the cookie, e.g. 2h or 3h30m. Default is browser session") f.StringVar(&c.CookieDomain, "cookie-domain", c.CookieDomain, "The optional domain parameter for the cookie") - f.StringVar(&c.SuccessUrl, "success-url", c.SuccessUrl, "The url to redirect after login") - f.StringVar(&c.LogoutUrl, "logout-url", c.LogoutUrl, "The url or path to redirect after logout") + f.StringVar(&c.SuccessURL, "success-url", c.SuccessURL, "The url to redirect after login") + f.StringVar(&c.LogoutURL, "logout-url", c.LogoutURL, "The url or path to redirect after logout") f.StringVar(&c.Template, "template", c.Template, "An alternative template for the login form") f.StringVar(&c.LoginPath, "login-path", c.LoginPath, "The path of the login resource") @@ -139,6 +141,7 @@ func (c *Config) ConfigureFlagSet(f *flag.FlagSet) { } } +// ReadConfig from the commandline args func ReadConfig() *Config { c, err := readConfig(flag.CommandLine, os.Args[1:]) if err != nil { diff --git a/login/config_test.go b/login/config_test.go @@ -47,14 +47,14 @@ func TestConfig_ReadConfig(t *testing.T) { TextLogging: true, JwtSecret: "jwtsecret", JwtExpiry: 42*time.Hour + 42*time.Minute, - SuccessUrl: "successurl", - LogoutUrl: "logouturl", + SuccessURL: "successurl", + LogoutURL: "logouturl", Template: "template", LoginPath: "loginpath", CookieName: "cookiename", CookieExpiry: 23 * time.Minute, CookieDomain: "*.example.com", - CookieHttpOnly: false, + CookieHTTPOnly: false, Backends: Options{ "simple": map[string]string{}, "foo": map[string]string{}, @@ -97,14 +97,14 @@ func TestConfig_ReadConfigFromEnv(t *testing.T) { TextLogging: true, JwtSecret: "jwtsecret", JwtExpiry: 42*time.Hour + 42*time.Minute, - SuccessUrl: "successurl", - LogoutUrl: "logouturl", + SuccessURL: "successurl", + LogoutURL: "logouturl", Template: "template", LoginPath: "loginpath", CookieName: "cookiename", CookieExpiry: 23 * time.Minute, CookieDomain: "*.example.com", - CookieHttpOnly: false, + CookieHTTPOnly: false, Backends: Options{ "simple": map[string]string{ "foo": "bar", diff --git a/login/handler.go b/login/handler.go @@ -14,10 +14,12 @@ import ( "time" ) -const contentTypeHtml = "text/html; charset=utf-8" +const contentTypeHTML = "text/html; charset=utf-8" const contentTypeJWT = "application/jwt" const contentTypePlain = "text/plain" +// Handler is the mail login handler. +// It serves the login ressource and does the authentication against the backends or oauth provider. type Handler struct { backends []Backend oauth oauthManager @@ -27,7 +29,7 @@ type Handler struct { // NewHandler creates a login handler based on the supplied configuration. func NewHandler(config *Config) (*Handler, error) { if len(config.Backends) == 0 && len(config.Oauth) == 0 { - return nil, errors.New("No login backends or oauth provider configured!") + return nil, errors.New("No login backends or oauth provider configured") } backends := []Backend{} @@ -115,8 +117,8 @@ func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) { r.ParseForm() if r.Method == "DELETE" || r.FormValue("logout") == "true" { h.deleteToken(w) - if h.config.LogoutUrl != "" { - w.Header().Set("Location", h.config.LogoutUrl) + if h.config.LogoutURL != "" { + w.Header().Set("Location", h.config.LogoutURL) w.WriteHeader(303) return } @@ -187,11 +189,11 @@ func (h *Handler) respondAuthenticated(w http.ResponseWriter, r *http.Request, u h.respondError(w, r) return } - if wantHtml(r) { + if wantHTML(r) { cookie := &http.Cookie{ Name: h.config.CookieName, Value: token, - HttpOnly: h.config.CookieHttpOnly, + HttpOnly: h.config.CookieHTTPOnly, Path: "/", } if h.config.CookieExpiry != 0 { @@ -201,7 +203,7 @@ func (h *Handler) respondAuthenticated(w http.ResponseWriter, r *http.Request, u cookie.Domain = h.config.CookieDomain } http.SetCookie(w, cookie) - w.Header().Set("Location", h.config.SuccessUrl) + w.Header().Set("Location", h.config.SuccessURL) w.WriteHeader(303) return } @@ -238,7 +240,7 @@ func (h *Handler) getToken(r *http.Request) (userInfo model.UserInfo, valid bool } func (h *Handler) respondError(w http.ResponseWriter, r *http.Request) { - if wantHtml(r) { + if wantHTML(r) { username, _, _ := getCredentials(r) writeLoginForm(w, loginFormData{ @@ -264,8 +266,8 @@ func (h *Handler) respondNotFound(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) + if wantHTML(r) { + w.Header().Set("Content-Type", contentTypeHTML) w.WriteHeader(403) username, _, _ := getCredentials(r) writeLoginForm(w, @@ -281,7 +283,7 @@ func (h *Handler) respondAuthFailure(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Wrong credentials") } -func wantHtml(r *http.Request) bool { +func wantHTML(r *http.Request) bool { return strings.Contains(r.Header.Get("Accept"), "text/html") } diff --git a/login/handler_test.go b/login/handler_test.go @@ -15,9 +15,9 @@ import ( "time" ) -const TypeJson = "Content-Type: application/json" +const TypeJSON = "Content-Type: application/json" const TypeForm = "Content-Type: application/x-www-form-urlencoded" -const AcceptHtml = "Accept: text/html" +const AcceptHTML = "Accept: text/html" const AcceptJwt = "Accept: application/jwt" func testConfig() *Config { @@ -124,7 +124,7 @@ func TestHandler_404(t *testing.T) { func TestHandler_LoginJson(t *testing.T) { // success - recorder := call(req("POST", "/context/login", `{"username": "bob", "password": "secret"}`, TypeJson, AcceptJwt)) + recorder := call(req("POST", "/context/login", `{"username": "bob", "password": "secret"}`, TypeJSON, AcceptJwt)) Equal(t, 200, recorder.Code) Equal(t, recorder.Header().Get("Content-Type"), "application/jwt") @@ -135,7 +135,7 @@ func TestHandler_LoginJson(t *testing.T) { InDelta(t, time.Now().Add(DefaultConfig().JwtExpiry).Unix(), claims["exp"], 2) // wrong credentials - recorder = call(req("POST", "/context/login", `{"username": "bob", "password": "FOOOBAR"}`, TypeJson, AcceptJwt)) + recorder = call(req("POST", "/context/login", `{"username": "bob", "password": "FOOOBAR"}`, TypeJSON, AcceptJwt)) Equal(t, 403, recorder.Code) Equal(t, "Wrong credentials", recorder.Body.String()) } @@ -209,7 +209,7 @@ func TestHandler_HandleOauth(t *testing.T) { func TestHandler_LoginWeb(t *testing.T) { // redirectSuccess - recorder := call(req("POST", "/context/login", "username=bob&password=secret", TypeForm, AcceptHtml)) + recorder := call(req("POST", "/context/login", "username=bob&password=secret", TypeForm, AcceptHTML)) Equal(t, 303, recorder.Code) Equal(t, "/", recorder.Header().Get("Location")) @@ -231,7 +231,7 @@ func TestHandler_LoginWeb(t *testing.T) { InDelta(t, time.Now().Add(DefaultConfig().JwtExpiry).Unix(), claims["exp"], 2) // show the login form again after authentication failed - recorder = call(req("POST", "/context/login", "username=bob&password=FOOBAR", TypeForm, AcceptHtml)) + recorder = call(req("POST", "/context/login", "username=bob&password=FOOBAR", TypeForm, AcceptHTML)) Equal(t, 403, recorder.Code) Contains(t, recorder.Body.String(), `class="container"`) Equal(t, recorder.Header().Get("Set-Cookie"), "") @@ -267,9 +267,9 @@ func checkDeleteCookei(t *testing.T, h http.Header) { Equal(t, int64(0), cookie.Expires.Unix()) } -func TestHandler_CustomLogoutUrl(t *testing.T) { +func TestHandler_CustomLogoutURL(t *testing.T) { cfg := DefaultConfig() - cfg.LogoutUrl = "http://example.com" + cfg.LogoutURL = "http://example.com" h := &Handler{ oauth: oauth2.NewManager(), config: cfg, @@ -286,7 +286,7 @@ func TestHandler_LoginError(t *testing.T) { h := testHandlerWithError() // backend returning an error with result type == jwt - request := req("POST", "/context/login", `{"username": "bob", "password": "secret"}`, TypeJson, AcceptJwt) + request := req("POST", "/context/login", `{"username": "bob", "password": "secret"}`, TypeJSON, AcceptJwt) recorder := httptest.NewRecorder() h.ServeHTTP(recorder, request) @@ -295,7 +295,7 @@ func TestHandler_LoginError(t *testing.T) { Equal(t, recorder.Body.String(), "Internal Server Error") // backend returning an error with result type == html - request = req("POST", "/context/login", `{"username": "bob", "password": "secret"}`, TypeJson, AcceptHtml) + request = req("POST", "/context/login", `{"username": "bob", "password": "secret"}`, TypeJSON, AcceptHTML) recorder = httptest.NewRecorder() h.ServeHTTP(recorder, request) diff --git a/login/login_form.go b/login/login_form.go @@ -181,7 +181,7 @@ func writeLoginForm(w http.ResponseWriter, params loginFormData) { } w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") - w.Header().Set("Content-Type", contentTypeHtml) + w.Header().Set("Content-Type", contentTypeHTML) if params.Error { w.WriteHeader(500) } diff --git a/login/provider_description.go b/login/provider_description.go @@ -1,5 +1,6 @@ package login +// ProviderDescription holds the provider metadata for the help message. type ProviderDescription struct { // the name of the provider Name string diff --git a/login/simple_backend.go b/login/simple_backend.go @@ -5,6 +5,7 @@ import ( "github.com/tarent/loginsrv/model" ) +// SimpleProviderName const with the providers name const SimpleProviderName = "simple" func init() { @@ -16,6 +17,7 @@ func init() { SimpleBackendFactory) } +// SimpleBackendFactory returns a new configured SimpleBackend func SimpleBackendFactory(config map[string]string) (Backend, error) { userPassword := map[string]string{} for k, v := range config { @@ -39,6 +41,7 @@ func NewSimpleBackend(userPassword map[string]string) *SimpleBackend { } } +// Authenticate the user func (sb *SimpleBackend) Authenticate(username, password string) (bool, model.UserInfo, error) { if p, exist := sb.userPassword[username]; exist && p == password { return true, model.UserInfo{Sub: username}, nil diff --git a/model/user_info.go b/model/user_info.go @@ -5,6 +5,8 @@ import ( "time" ) +// UserInfo holds the parameters returned by the backends. +// This information wir be serialized to build the JWT token contents. type UserInfo struct { Sub string `json:"sub"` Picture string `json:"picture,omitempty"` @@ -14,8 +16,8 @@ type UserInfo struct { Expiry int64 `json:"exp,omitempty"` } -// this interface implementation -// lets us use the user info as Claim for jwt-go +// Valid lets us use the user info as Claim for jwt-go. +// It checks the token expiry. func (u UserInfo) Valid() error { if u.Expiry < time.Now().Unix() { return errors.New("token expired") diff --git a/oauth2/github.go b/oauth2/github.go @@ -9,12 +9,13 @@ import ( "strings" ) -var githubApi = "https://api.github.com" +var githubAPI = "https://api.github.com" func init() { RegisterProvider(providerGithub) } +// GithubUser is used for parsing the github response type GithubUser struct { Login string `json:"login,omitempty"` AvatarURL string `json:"avatar_url,omitempty"` @@ -28,7 +29,7 @@ var providerGithub = Provider{ TokenURL: "https://github.com/login/oauth/access_token", GetUserInfo: func(token TokenInfo) (model.UserInfo, string, error) { gu := GithubUser{} - url := fmt.Sprintf("%v/user?access_token=%v", githubApi, token.AccessToken) + url := fmt.Sprintf("%v/user?access_token=%v", githubAPI, token.AccessToken) resp, err := http.Get(url) if err != nil { return model.UserInfo{}, "", err diff --git a/oauth2/github_test.go b/oauth2/github_test.go @@ -48,12 +48,12 @@ func Test_Github_getUserInfo(t *testing.T) { })) defer server.Close() - githubApi = server.URL + githubAPI = server.URL - u, rawJson, err := providerGithub.GetUserInfo(TokenInfo{AccessToken: "secret"}) + u, rawJSON, err := providerGithub.GetUserInfo(TokenInfo{AccessToken: "secret"}) NoError(t, err) Equal(t, "octocat", u.Sub) Equal(t, "octocat@github.com", u.Email) Equal(t, "monalisa octocat", u.Name) - Equal(t, githubTestUserResponse, rawJson) + Equal(t, githubTestUserResponse, rawJSON) } diff --git a/oauth2/manager.go b/oauth2/manager.go @@ -65,6 +65,8 @@ func (manager *Manager) Handle(w http.ResponseWriter, r *http.Request) ( return true, false, model.UserInfo{}, nil } +// GetConfigFromRequest returns the oauth configuration matching the current path. +// The configuration name is taken from the last path segment. func (manager *Manager) GetConfigFromRequest(r *http.Request) (Config, error) { configName := manager.getConfigNameFromPath(r.URL.Path) cfg, exist := manager.configs[configName] @@ -73,7 +75,7 @@ func (manager *Manager) GetConfigFromRequest(r *http.Request) (Config, error) { } if cfg.RedirectURI == "" { - cfg.RedirectURI = redirectUriFromRequest(r) + cfg.RedirectURI = redirectURIFromRequest(r) } return cfg, nil @@ -98,17 +100,17 @@ func (manager *Manager) AddConfig(providerName string, opts map[string]string) e TokenURL: p.TokenURL, } - if clientId, exist := opts["client_id"]; !exist { + clientID, exist := opts["client_id"] + if !exist { return fmt.Errorf("missing parameter client_id") - } else { - cfg.ClientID = clientId } + cfg.ClientID = clientID - if clientSecret, exist := opts["client_secret"]; !exist { + clientSecret, exist := opts["client_secret"] + if !exist { return fmt.Errorf("missing parameter client_secret") - } else { - cfg.ClientSecret = clientSecret } + cfg.ClientSecret = clientSecret if scope, exist := opts["scope"]; exist { cfg.Scope = scope @@ -122,11 +124,12 @@ func (manager *Manager) AddConfig(providerName string, opts map[string]string) e return nil } +// GetConfigs of the manager func (manager *Manager) GetConfigs() map[string]Config { return manager.configs } -func redirectUriFromRequest(r *http.Request) string { +func redirectURIFromRequest(r *http.Request) string { u := url.URL{} u.Path = r.URL.Path diff --git a/oauth2/manager_test.go b/oauth2/manager_test.go @@ -217,7 +217,7 @@ func Test_Manager_redirectUriFromRequest(t *testing.T) { if test.tls { r.TLS = &tls.ConnectionState{} } - uri := redirectUriFromRequest(r) + uri := redirectURIFromRequest(r) Equal(t, test.expected, uri) }) } @@ -237,12 +237,12 @@ func Test_Manager_RedirectURI_Generation(t *testing.T) { startFlowReceivedConfig = cfg } - callUrl := "http://example.com/login/github" - r, _ := http.NewRequest("GET", callUrl, nil) + callURL := "http://example.com/login/github" + r, _ := http.NewRequest("GET", callURL, nil) _, _, _, err := m.Handle(httptest.NewRecorder(), r) NoError(t, err) - Equal(t, callUrl, startFlowReceivedConfig.RedirectURI) + Equal(t, callURL, startFlowReceivedConfig.RedirectURI) } func assertEqualConfig(t *testing.T, c1, c2 Config) { diff --git a/oauth2/oauth.go b/oauth2/oauth.go @@ -57,8 +57,8 @@ type TokenInfo struct { Scope string `json:"scope,omitempty"` } -// JsonError represents an oauth error response in json form. -type JsonError struct { +// JSONError represents an oauth error response in json form. +type JSONError struct { Error string `json:"error"` } @@ -83,8 +83,8 @@ func StartFlow(cfg Config, w http.ResponseWriter) { HttpOnly: true, }) - targetUrl := cfg.AuthURL + "?" + values.Encode() - w.Header().Set("Location", targetUrl) + targetURL := cfg.AuthURL + "?" + values.Encode() + w.Header().Set("Location", targetURL) w.WriteHeader(http.StatusFound) } @@ -133,7 +133,7 @@ func getAccessToken(cfg Config, state, code string) (TokenInfo, error) { return TokenInfo{}, fmt.Errorf("error reading token exchange response: %q", err) } - jsonError := JsonError{} + jsonError := JSONError{} json.Unmarshal(body, &jsonError) if jsonError.Error != "" { return TokenInfo{}, fmt.Errorf("error: got %q on token exchange", jsonError.Error) diff --git a/oauth2/oauth_test.go b/oauth2/oauth_test.go @@ -74,12 +74,12 @@ func Test_Authenticate(t *testing.T) { func Test_Authenticate_CodeExchangeError(t *testing.T) { var testReturnCode int - testResponseJson := `{"error":"bad_verification_code","error_description":"The code passed is incorrect or expired.","error_uri":"https://developer.github.com/v3/oauth/#bad-verification-code"}` + testResponseJSON := `{"error":"bad_verification_code","error_description":"The code passed is incorrect or expired.","error_uri":"https://developer.github.com/v3/oauth/#bad-verification-code"}` // mock a server for token exchange server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(testReturnCode) w.Header().Set("Content-Type", "application/json") - w.Write([]byte(testResponseJson)) + w.Write([]byte(testResponseJSON)) })) defer server.Close() @@ -103,7 +103,7 @@ func Test_Authenticate_CodeExchangeError(t *testing.T) { Equal(t, "", tokenInfo.AccessToken) testReturnCode = 200 - testResponseJson = `{"foo": "bar"}` + testResponseJSON = `{"foo": "bar"}` tokenInfo, err = Authenticate(testConfigCopy, request) Error(t, err) EqualError(t, err, `error: no access_token on token exchange`) diff --git a/oauth2/provider.go b/oauth2/provider.go @@ -4,7 +4,7 @@ import ( "github.com/tarent/loginsrv/model" ) -// Oauth provider configuration +// Provider is the descriptoin of an oauth provider adapter type Provider struct { // The name to access the provider in the configuration Name string @@ -29,7 +29,7 @@ func RegisterProvider(p Provider) { provider[p.Name] = p } -// Unregister an Oauth provider +// UnRegisterProvider removes a provider func UnRegisterProvider(name string) { delete(provider, name) } diff --git a/osiam/backend.go b/osiam/backend.go @@ -7,28 +7,30 @@ import ( "net/url" ) +// Backend is the osiam authentication backend. type Backend struct { client *Client } // NewBackend creates a new OSIAM Backend and verifies the parameters. -func NewBackend(endpoint, clientId, clientSecret string) (*Backend, error) { +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) } - if clientId == "" { - return nil, errors.New("No osiam clientId provided.") + if clientID == "" { + return nil, errors.New("no osiam clientID provided.") } if clientSecret == "" { - return nil, errors.New("No osiam clientSecret provided.") + return nil, errors.New("no osiam clientSecret provided") } - client := NewClient(endpoint, clientId, clientSecret) + client := NewClient(endpoint, clientID, clientSecret) return &Backend{ client: client, }, nil } +// Authenticate the user func (b *Backend) Authenticate(username, password string) (bool, model.UserInfo, error) { authenticated, _, err := b.client.GetTokenByPassword(username, password) if !authenticated || err != nil { diff --git a/osiam/client.go b/osiam/client.go @@ -9,21 +9,23 @@ import ( "strings" ) +// Client is a wrapper for the osiam API. type Client struct { Endpoint string - ClientId string + ClientID string ClientSecret string } -func NewClient(endpoint string, clientId string, clientSecret string) *Client { +// NewClient for the osiam API. +func NewClient(endpoint string, clientID string, clientSecret string) *Client { return &Client{ Endpoint: endpoint, - ClientId: clientId, + ClientID: clientID, ClientSecret: clientSecret, } } -// Do an Osiam authorisation by Resource Owner Password Credentials Grant. +// GetTokenByPassword does an Osiam authorisation by Resource Owner Password Credentials Grant. // If no scopes are supplied, the default scope is 'me'. func (c *Client) GetTokenByPassword(username, password string, scopes ...string) (authenticated bool, token *Token, err error) { scopeList := strings.Join(scopes, ",") @@ -37,7 +39,7 @@ func (c *Client) GetTokenByPassword(username, password string, scopes ...string) return false, nil, err } - req.SetBasicAuth(c.ClientId, c.ClientSecret) + req.SetBasicAuth(c.ClientID, c.ClientSecret) req.Header.Set("Content-type", "application/x-www-form-urlencoded") res, err := http.DefaultClient.Do(req) @@ -50,7 +52,7 @@ func (c *Client) GetTokenByPassword(username, password string, scopes ...string) return false, nil, err } - if !isJson(res.Header.Get("Content-Type")) { + if !isJSON(res.Header.Get("Content-Type")) { bodyStart := string(body) if len(bodyStart) > 50 { bodyStart = bodyStart[0:50] @@ -80,6 +82,6 @@ func (c *Client) GetTokenByPassword(username, password string, scopes ...string) return false, nil, fmt.Errorf("Osiam error: %v, %v (http status %v)", errorMessage.Error, errorMessage.Message, res.StatusCode) } -func isJson(contentType string) bool { +func isJSON(contentType string) bool { return strings.HasPrefix(contentType, "application/json") } diff --git a/osiam/client_test.go b/osiam/client_test.go @@ -25,7 +25,7 @@ func TestClient_GetTokenByPassword(t *testing.T) { ExpiresIn: 28493, ExpiresAt: Timestamp{time.Unix(1479600891424, 0)}, RefreshToken: "15b22304-f838-48c2-9c40-18bf285060a6", - ClientId: "example-client", + ClientID: "example-client", RefreshTokenExpiresAt: Timestamp{time.Unix(1479575304893, 0)}, UserName: "admin", TokenType: "bearer", diff --git a/osiam/error.go b/osiam/error.go @@ -4,11 +4,13 @@ import ( "encoding/json" ) +// OsiamError represents an error response from osiam. type OsiamError struct { Error string Message string } +// ParseOsiamError creates an OsiamError out of a json func ParseOsiamError(jsonBody []byte) OsiamError { m := map[string]interface{}{} err := json.Unmarshal(jsonBody, &m) @@ -46,10 +48,12 @@ func ParseOsiamError(jsonBody []byte) OsiamError { return e } +// IsLoginError checks, if the grant was invalid func (e OsiamError) IsLoginError() bool { return e.Error == "invalid_grant" } +// IsUnauthorized checks, if the error reason was Unauthorized func (e OsiamError) IsUnauthorized() bool { return e.Error == "Unauthorized" } diff --git a/osiam/setup.go b/osiam/setup.go @@ -5,6 +5,7 @@ import ( "github.com/tarent/loginsrv/login" ) +// OsiamProviderName const with the name of the provider const OsiamProviderName = "osiam" func init() { diff --git a/osiam/token.go b/osiam/token.go @@ -11,7 +11,7 @@ type Token struct { TokenType string `json:"token_type"` // example "bearer" AccessToken string `json:"access_token"` // example "79f479c2-c0d7-458a-8464-7eb887dbc943" RefreshToken string `json:"refresh_token"` // example "3c7c4a87-dc91-4dd0-8ec8-d229a237a47c" - ClientId string `json:"client_id"` // example "example-client" + ClientID string `json:"client_id"` // example "example-client" UserName string `json:"user_name"` // example "admin" Userid string `json:"user_id"` // example "84f6cffa-4505-48ec-a851-424160892283" Scope string `json:"scope"` // example "ME" @@ -20,10 +20,12 @@ type Token struct { ExpiresIn int `json:"expires_in"` // example 28795 } +// Timestamp is a helper class for json handling ht the timestamp type Timestamp struct { T time.Time } +// UnmarshalJSON does the unmarshaling func (timestamp *Timestamp) UnmarshalJSON(b []byte) (err error) { i, err := strconv.ParseInt(string(b), 10, 64) if err != nil { @@ -33,6 +35,7 @@ func (timestamp *Timestamp) UnmarshalJSON(b []byte) (err error) { return nil } +// MarshalJSON does the marshaling func (timestamp *Timestamp) MarshalJSON() ([]byte, error) { if timestamp.T.UnixNano() == nilTime { return []byte("null"), nil diff --git a/osiam/token_test.go b/osiam/token_test.go @@ -12,7 +12,7 @@ var testToken = &Token{ ExpiresIn: 28493, ExpiresAt: Timestamp{time.Unix(1479600891424, 0)}, RefreshToken: "15b22304-f838-48c2-9c40-18bf285060a6", - ClientId: "example-client", + ClientID: "example-client", RefreshTokenExpiresAt: Timestamp{time.Unix(1479575304893, 0)}, UserName: "admin", TokenType: "bearer",