package user import ( "errors" "strconv" "strings" "github.com/kataras/iris" "github.com/kataras/iris/mvc" "github.com/kataras/iris/sessions" ) const sessionIDKey = "UserID" // paths var ( PathLogin = mvc.Response{Path: "/user/login"} PathLogout = mvc.Response{Path: "/user/logout"} ) // AuthController is the user authentication controller, a custom shared controller. type AuthController struct { // context is auto-binded if struct depends on this, // in this controller we don't we do everything with mvc-style, // and that's neither the 30% of its features. // Ctx iris.Context Source *DataSource Session *sessions.Session // the whole controller is request-scoped because we already depend on Session, so // this will be new for each new incoming request, BeginRequest sets that based on the session. UserID int64 } // BeginRequest saves login state to the context, the user id. func (c *AuthController) BeginRequest(ctx iris.Context) { c.UserID, _ = c.Session.GetInt64(sessionIDKey) } // EndRequest is here just to complete the BaseController // in order to be tell iris to call the `BeginRequest` before the main method. func (c *AuthController) EndRequest(ctx iris.Context) {} func (c *AuthController) fireError(err error) mvc.View { return mvc.View{ Code: iris.StatusBadRequest, Name: "shared/error.html", Data: iris.Map{"Title": "User Error", "Message": strings.ToUpper(err.Error())}, } } func (c *AuthController) redirectTo(id int64) mvc.Response { return mvc.Response{Path: "/user/" + strconv.Itoa(int(id))} } func (c *AuthController) createOrUpdate(firstname, username, password string) (user Model, err error) { username = strings.Trim(username, " ") if username == "" || password == "" || firstname == "" { return user, errors.New("empty firstname, username or/and password") } userToInsert := Model{ Firstname: firstname, Username: username, password: password, } // password is hashed by the Source. newUser, err := c.Source.InsertOrUpdate(userToInsert) if err != nil { return user, err } return newUser, nil } func (c *AuthController) isLoggedIn() bool { // we don't search by session, we have the user id // already by the `BeginRequest` middleware. return c.UserID > 0 } func (c *AuthController) verify(username, password string) (user Model, err error) { if username == "" || password == "" { return user, errors.New("please fill both username and password fields") } u, found := c.Source.GetByUsername(username) if !found { // if user found with that username not found at all. return user, errors.New("user with that username does not exist") } if ok, err := ValidatePassword(password, u.HashedPassword); err != nil || !ok { // if user found but an error occurred or the password is not valid. return user, errors.New("please try to login with valid credentials") } return u, nil } // if logged in then destroy the session // and redirect to the login page // otherwise redirect to the registration page. func (c *AuthController) logout() mvc.Response { if c.isLoggedIn() { c.Session.Destroy() } return PathLogin }