func (m *Manager) showLoginForm(lctx *LoginFormContext, w http.ResponseWriter, r *http.Request) { logger := msg logger.Log( "func", "showLoginForm (Manager.GetEndpoints)") // build action query ar := getOsinAuthRequest(lctx.Context) // presume the context has *osin.AuthorizeRequest aq := url.Values{} aq.Add("response_type", string(ar.Type)) aq.Add("client_id", ar.Client.GetId()) aq.Add("state", ar.State) aq.Add("scope", ar.Scope) aq.Add("redirect_uri", ar.RedirectUri) // form action url aurl := r.URL aurl.RawQuery = aq.Encode() logger.Log( "func", "showLoginForm (Manager.GetEndpoints)", "action url", aurl) lctx.ActionURL = aurl w.Header().Add("Content-Type", "text/html;charset=utf8") if err := m.loginFormFunc(lctx); err != nil { serr := store.ExpandError(err) logger.Log( "func", "showLoginForm (Manager.GetEndpoints)", "action url", aurl, "error", serr.ServerMsg) } }
// NewUserFunc creates the default parser of login HTTP request func NewUserFunc(idName string) UserFunc { return func(r *http.Request, us store.Store) (ou OAuth2User, err error) { var c store.Conds id := r.Form.Get(idName) if id == "" { serr := store.Error(http.StatusBadRequest, "empty user identifier") err = serr return } // different condition based on the user_id field format if govalidator.IsEmail(id) { c = store.NewConds().Add("email", id) } else { c = store.NewConds().Add("username", id) } // get user from database u := us.AllocEntity() err = us.One(c, u) if err != nil { serr := store.ExpandError(err) if serr.Status != http.StatusNotFound { serr.TellServer("Error searching user %#v: %s", id, serr.ServerMsg) return } err = serr return } // if user does not exists if u == nil { serr := store.Error(http.StatusBadRequest, "Username or Password incorrect") serr.TellServer("Unknown user %#v attempt to login", id) err = serr return } // cast the user as OAuth2User // and do password check ou, ok := u.(OAuth2User) if !ok { serr := store.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) serr.TellServer("User cannot be cast as OAuth2User") err = serr return } return } }
// jsonErrorEncoder expands given error to StoreError then encode to JSON func jsonErrorEncoder(ctx context.Context, err error, w http.ResponseWriter) { w.Header().Add("Content-Type", "application/json") // quick fix for gokit bad request wrapping problem switch err.(type) { case httptransport.Error: err = err.(httptransport.Error).Err } serr := store.ExpandError(err) log.Printf("error: %#v", serr.ServerMsg) json.NewEncoder(w).Encode(serr) }
// GetClient implements osin.Storage.GetClient func (storage *Storage) GetClient(id string) (c osin.Client, err error) { // TODO: use logger := log.NewContext(,sg) logger, errLogger := msg, errMsg logger.Log( "method", "GetClient", "id", id) srv, err := store.Get(storage.ctx, KeyClient) if err != nil { serr := store.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) serr.TellServer("unable to get client store: %s", err) err = serr return } defer srv.Close() e := &Client{} conds := store.NewConds() conds.Add("id", id) err = srv.One(conds, e) if err != nil { serr := store.ExpandError(err) errLogger.Log( "method", "GetClient", "id", id, "cond", conds, "message", "Failed running One()", "error", serr.ServerMsg) return } else if e == nil { errLogger.Log( "method", "GetClient", "id", id, "cond", fmt.Sprintf("%#v", conds), "message", "Client not found") err = store.Error(http.StatusNotFound, "Client not found for the given id") return } c = e return }
func TestParseError_User(t *testing.T) { var code int var msg string userMsg := "Some user error" var err error = errors.New(userMsg) code, msg = store.ParseError(err) if code != http.StatusInternalServerError { t.Errorf("Incorrect status code. Expecting %d but get %d", http.StatusInternalServerError, code) } if msg != userMsg { t.Errorf("Incorrect status message. Expecting %s but get %s", userMsg, msg) } serr := store.ExpandError(err) if serr.Status != http.StatusInternalServerError { t.Errorf("Incorrect StoreError.Status. Expecting %#v but get %#v", http.StatusInternalServerError, serr.Status) } if serr.Code != http.StatusInternalServerError { t.Errorf("Incorrect StoreError.Code. Expecting %#v but get %#v", http.StatusInternalServerError, serr.Code) } if serr.ServerMsg != userMsg { t.Errorf("Incorrect StoreError.ServerMsg. Expecting %#v but get %#v", userMsg, serr.ServerMsg) } if serr.ClientMsg != userMsg { t.Errorf("Incorrect StoreError.ClientMsg. Expecting %#v but get %#v", userMsg, serr.ClientMsg) } if serr.DeveloperMsg != "" { t.Errorf("Incorrect StoreError.DeveloperMsg. Expecting %#v but get %#v", "", serr.DeveloperMsg) } }
func TestParseError_Singletons(t *testing.T) { var code int var msg string var err error = store.ErrorNotFound msgExpect := err.Error() code, msg = store.ParseError(err) if code != http.StatusNotFound { t.Errorf("Incorrect status code. Expecting %d but get %d", http.StatusNotFound, code) } if msg != msgExpect { t.Errorf("Incorrect status message. Expecting %s but get %s", msgExpect, msg) } serr := store.ExpandError(err) if serr.Status != http.StatusNotFound { t.Errorf("Incorrect StoreError.Status. Expecting %#v but get %#v", http.StatusNotFound, serr.Status) } if serr.Code != http.StatusNotFound { t.Errorf("Incorrect StoreError.Code. Expecting %#v but get %#v", http.StatusNotFound, serr.Code) } if serr.ServerMsg != msgExpect { t.Errorf("Incorrect StoreError.ServerMsg. Expecting %#v but get %#v", msgExpect, serr.ServerMsg) } if serr.ClientMsg != msgExpect { t.Errorf("Incorrect StoreError.ClientMsg. Expecting %#v but get %#v", msgExpect, serr.ClientMsg) } if serr.DeveloperMsg != "" { t.Errorf("Incorrect StoreError.DeveloperMsg. Expecting %#v but get %#v", "", serr.DeveloperMsg) } }
// NewLoginFormFunc creates a LoginFormFunc from given template func NewLoginFormFunc(idName, tpl string) LoginFormFunc { // compile template for login form loginTpl, err := template.New("loginForm").Parse(tpl) if err != nil { panic(err) // should not happen, simply panic } return func(lctx *LoginFormContext) (err error) { // TODO: pass the login error into showLoginForm context // and display it to the visitor // template variables vars := map[string]interface{}{ "Title": "Login", "FormAction": lctx.ActionURL, "UserID": "", "TextUserID": "Login ID or Email", "TextPassword": "******", "TextSubmit": "Login", } if lctx.Request.Method == "POST" && lctx.LoginErr != nil { vars["LoginErr"] = lctx.LoginErr.Error() vars["UserID"] = lctx.Request.Form.Get(idName) } // render the form with vars err = loginTpl.Execute(lctx.ResponseWriter, vars) if err != nil { serr := store.ExpandError(err) serr.TellServer("error executing login template: %#v", err.Error()) err = serr return } return } }
func TestError(t *testing.T) { estatus, ecode, emsg := testErrorCode() var err error = store.Error(ecode, emsg) code, msg := store.ParseError(err) if code != ecode { t.Errorf("code output not correct. Expect %#v but get %#v", ecode, code) } if msg != emsg { t.Errorf("msg output not correct. Expect %#v but get %#v", emsg, msg) } serr := store.ExpandError(err) if serr.Status != estatus { t.Errorf("Incorrect StoreError.Status. Expecting %#v but get %#v", estatus, serr.Status) } if serr.Code != ecode { t.Errorf("Incorrect StoreError.Code. Expecting %#v but get %#v", ecode, serr.Code) } if serr.ServerMsg != emsg { t.Errorf("Incorrect StoreError.ServerMsg. Expecting %#v but get %#v", emsg, serr.ServerMsg) } if serr.ClientMsg != emsg { t.Errorf("Incorrect StoreError.ClientMsg. Expecting %#v but get %#v", emsg, serr.ClientMsg) } if serr.DeveloperMsg != "" { t.Errorf("Incorrect StoreError.DeveloperMsg. Expecting %#v but get %#v", "", serr.DeveloperMsg) } }
// GetEndpoints generate endpoints http handers and return func (m *Manager) GetEndpoints(factory store.Factory) *Endpoints { // try to login with given request login tryLogin := func(ctx context.Context, r *http.Request) (user OAuth2User, err error) { logger := msg logger.Log( "func", "tryLogin (Manager.GetEndpoints)") // parse POST input r.ParseForm() if r.Method == "POST" { var u OAuth2User var us store.Store // get and check password non-empty password := r.Form.Get("password") if password == "" { err = errors.New("empty password") return } // obtain user store us, err = store.Get(ctx, KeyUser) if err != nil { err = store.Error( http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)). TellServer("error obtaining user store: %s", err.Error()) return } // get user by userFunc u, err = m.userFunc(r, us) if err != nil { serr := store.ExpandError(err) if serr.Status == http.StatusNotFound { err = store.Error(http.StatusBadRequest, "user id or password incorrect"). TellServer("user not found") } else { err = store.Error( http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)). TellServer("error obtaining user: %s", serr.ServerMsg) } return } // if user is nil, user not found if u == nil { err = store.Error(http.StatusBadRequest, "user not found") return } // if password does not match if !u.PasswordIs(password) { err = store.Error(http.StatusBadRequest, "user id or password incorrect"). TellServer("incorrect password") return } // return pointer of user object, allow it to be re-cast logger.Log( "func", "tryLogin (Manager.GetEndpoints)", "message", "login success") user = u return } // no POST input or incorrect login, show form // end login handling sequence and wait for // user input from login form err = store.Error(http.StatusUnauthorized, "Require login"). TellServer("no POST input") return } type ContextHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request) *osin.Response // sessionContext takes a ContextHandlerFunc and returns // a http.HandlerFunc sessionContext := func(inner ContextHandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // per connection based context.Context, with factory ctx := store.WithFactory(context.Background(), factory) defer store.CloseAllIn(ctx) if resp := inner(ctx, w, r); resp != nil { if resp.InternalError != nil { errLogger := errMsg errLogger.Log( "func", "sessionContext (Manager.GetEndpoints)", "error", resp.InternalError.Error()) } osin.OutputJSON(resp, w, r) } } } ep := Endpoints{} // authorize endpoint ep.Auth = sessionContext(func(ctx context.Context, w http.ResponseWriter, r *http.Request) *osin.Response { logger := msg logger.Log( "endpoint", "auth") srvr := m.osinServer resp := srvr.NewResponse() resp.Storage.(*Storage).SetContext(ctx) // handle authorize request with osin if ar := srvr.HandleAuthorizeRequest(resp, r); ar != nil { logger.Log( "endpoint", "auth", "message", "handle authorize request") // TODO: maybe redirect to another URL for // dedicated login form flow? var err error if ar.UserData, err = tryLogin(ctx, r); err != nil { serr := store.ExpandError(err) logger.Log( "endpoint", "auth", "message", "handle authorize request", "error", serr.ServerMsg) lctx := &LoginFormContext{ Context: withOsinAuthRequest(ctx, ar), LoginErr: err, ResponseWriter: w, Request: r, Logger: logger, } m.showLoginForm(lctx, w, r) return nil } logger.Log( "endpoint", "auth", "message", "User obtained", "osin.AuthorizeData.UserData", fmt.Sprintf("%#v", ar.UserData)) ar.Authorized = true srvr.FinishAuthorizeRequest(resp, r, ar) } logger.Log( "endpoint", "auth", "message", "User obtained", "response", fmt.Sprintf("%#v", resp)) return resp }) // token endpoint ep.Token = sessionContext(func(ctx context.Context, w http.ResponseWriter, r *http.Request) *osin.Response { logger := msg logger.Log( "endpoint", "token") srvr := m.osinServer resp := srvr.NewResponse() resp.Storage.(*Storage).SetContext(ctx) if ar := srvr.HandleAccessRequest(resp, r); ar != nil { // TODO: handle authorization // check if the user has the permission to grant the scope logger.Log( "endpoint", "token", "message", "access successful") ar.Authorized = true srvr.FinishAccessRequest(resp, r, ar) } logger.Log( "endpoint", "token", "response", fmt.Sprintf("%#v", resp)) return resp }) // information endpoint ep.Info = sessionContext(func(ctx context.Context, w http.ResponseWriter, r *http.Request) *osin.Response { logger := msg logger.Log( "endpoint", "information") srvr := m.osinServer resp := srvr.NewResponse() resp.Storage.(*Storage).SetContext(ctx) defer resp.Close() if ir := srvr.HandleInfoRequest(resp, r); ir != nil { srvr.FinishInfoRequest(resp, r, ir) } logger.Log( "endpoint", "information", "response", fmt.Sprintf("%#v", resp)) return resp }) return &ep }
// SaveAccess writes AccessData. // If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh. func (storage *Storage) SaveAccess(ad *osin.AccessData) (err error) { // TODO: use logger := log.NewContext(,sg) logger, errLogger := msg, errMsg logger.Log( "method", "SaveAccess", "*osin.AccessData", ad) srv, err := store.Get(storage.ctx, KeyAccess) if err != nil { return } defer srv.Close() // generate database access type e := &AccessData{} err = e.ReadOsin(ad) if err != nil { return } // store client id with access in database e.ClientID = e.Client.GetId() // if AuthorizeData is set, store as JSON if ad.AuthorizeData != nil { var b []byte authData := &AuthorizeData{} if err = authData.ReadOsin(ad.AuthorizeData); err != nil { return } if b, err = json.Marshal(authData); err != nil { return } e.AuthorizeDataJSON = string(b) } // if AccessData is set, store as JSON if ad.AccessData != nil { var b []byte accessData := &AccessData{} if err = accessData.ReadOsin(ad.AccessData); err != nil { return } if accessData.AccessData != nil { // forget data of too long ago accessData.AccessData = nil } if b, err = json.Marshal(accessData); err != nil { return } e.AccessDataJSON = string(b) } // create in database if err = srv.Create(store.NewConds(), e); err != nil { serr := store.ExpandError(err) errLogger.Log( "method", "SaveAccess", "*osin.AccessData", ad, "err", serr.ServerMsg) } return }