From bd20a87548e7dadced66e19ac197d6a7c9b7d829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Tue, 20 Feb 2018 23:22:32 +0100 Subject: [PATCH] Add user IP to its token --- backend/auth/auth.go | 4 ++- backend/auth/middleware.go | 60 ++++++++++++++++++++++++++++++++++++-- backend/main.go | 2 ++ backend/tokens/tokens.go | 17 ++++++----- backend/users/handlers.go | 2 +- 5 files changed, 74 insertions(+), 11 deletions(-) diff --git a/backend/auth/auth.go b/backend/auth/auth.go index 6b3c331..08eb044 100644 --- a/backend/auth/auth.go +++ b/backend/auth/auth.go @@ -71,7 +71,7 @@ func (a *Authorizer) GenHash(password string) (string, error) { } // Login cheks password and creates a jwt token -func (a *Authorizer) Login(username, password string) (*tokens.Token, error) { +func (a *Authorizer) Login(r *http.Request, username, password string) (*tokens.Token, error) { u, err := a.Backend.GetUser(username) if err != nil { return nil, err @@ -104,6 +104,7 @@ func (a *Authorizer) Login(username, password string) (*tokens.Token, error) { t := &tokens.Token{ Token: ss, Username: u.GetName(), + IP: getIPFromRequest(r), } if err := t.Add(a.db); err != nil { @@ -168,6 +169,7 @@ func (a *Authorizer) CurrentUser(rw http.ResponseWriter, req *http.Request) (Use } token.UserAgent = req.UserAgent() + token.IP = getIPFromRequest(req) if err := token.Update(a.db); err != nil { return nil, err } diff --git a/backend/auth/middleware.go b/backend/auth/middleware.go index c4c9f35..a3e8469 100644 --- a/backend/auth/middleware.go +++ b/backend/auth/middleware.go @@ -2,19 +2,21 @@ package auth import ( "context" + "net" "net/http" "github.com/sirupsen/logrus" ) +type ipContextKey string +type authContextKey string + // Middleware get User from session and put it in context type Middleware struct { authorizer *Authorizer log *logrus.Entry } -type authContextKey string - // NewMiddleware returns a new authentication middleware func NewMiddleware(authorizer *Authorizer, log *logrus.Entry) *Middleware { return &Middleware{ @@ -101,3 +103,57 @@ func GetCurrentUser(r *http.Request, log *logrus.Entry) User { } return user } + +// IPMiddleware set the IP in the request context +type IPMiddleware struct { + log *logrus.Entry +} + +// NewIPMiddleware returns a new ip middleware +func NewIPMiddleware(log *logrus.Entry) *IPMiddleware { + return &IPMiddleware{ + log: log.WithField("middleware", "ip"), + } +} + +func (m *IPMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + ip := getRequestIP(r) + + ctxKey := ipContextKey("ip") + ctx := context.WithValue(r.Context(), ctxKey, ip) + r = r.WithContext(ctx) + + next(w, r) +} + +func getRequestIP(req *http.Request) string { + // Try to get the IP from this header + var ip = req.Header.Get("X-Real-IP") + if ip != "" { + return ip + } + + // Or from this one + ip = req.Header.Get("X-Forwarded-For") + if ip != "" { + return ip + } + + host, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + // fake result + return "0.0.0.0" + } + + hostIP := net.ParseIP(host) + if host == "" { + return "0.0.0.0" + } + + // Default to the IP from the request + return hostIP.String() +} + +func getIPFromRequest(r *http.Request) string { + return r.Context().Value(ipContextKey("ip")).(string) +} diff --git a/backend/main.go b/backend/main.go index 19b0416..da77cf0 100644 --- a/backend/main.go +++ b/backend/main.go @@ -89,6 +89,8 @@ func main() { defer c.Stop() n := negroni.Classic() + // Middleware for setting ips + n.Use(auth.NewIPMiddleware(log)) // Middleware for authentication n.Use(authMiddleware) // Serve static files diff --git a/backend/tokens/tokens.go b/backend/tokens/tokens.go index d25f24a..e76c366 100644 --- a/backend/tokens/tokens.go +++ b/backend/tokens/tokens.go @@ -4,18 +4,19 @@ import ( "database/sql" "errors" "fmt" + "time" "github.com/jmoiron/sqlx" "gitlab.quimbo.fr/odwrtw/canape/backend/sqly" ) const ( - addTokenQuery = `INSERT INTO tokens (token, username) VALUES ($1, $2);` + addTokenQuery = `INSERT INTO tokens (token, username, ip) VALUES ($1, $2, $3);` getTokenQuery = `SELECT * FROM tokens WHERE token=$1;` getUserTokenQuery = `SELECT * FROM tokens WHERE username=$1 and token=$2;` getUserTokensQuery = `SELECT * FROM tokens WHERE username=$1;` deleteTokenQuery = `DELETE FROM tokens WHERE username=$1 AND token=$2;` - updateTokenQuery = `UPDATE tokens SET description=:description, user_agent=:user_agent WHERE token=:token RETURNING *;` + updateTokenQuery = `UPDATE tokens SET description=:description, user_agent=:user_agent, ip=:ip, last_used=now() WHERE token=:token RETURNING *;` ) // Custom errors @@ -26,15 +27,17 @@ var ( // Token represents a token type Token struct { sqly.BaseModel - Username string `db:"username" json:"username"` - Token string `db:"token" json:"token"` - Description string `db:"description" json:"description"` - UserAgent string `db:"user_agent" json:"user_agent"` + Username string `db:"username" json:"username"` + Token string `db:"token" json:"token"` + Description string `db:"description" json:"description"` + UserAgent string `db:"user_agent" json:"user_agent"` + IP string `db:"ip" json:"ip"` + LastUsed time.Time `db:"last_used" json:"last_used"` } // Add a token to the database func (t *Token) Add(db *sqlx.DB) error { - _, err := db.Queryx(addTokenQuery, t.Token, t.Username) + _, err := db.Queryx(addTokenQuery, t.Token, t.Username, t.IP) if err != nil { return err } diff --git a/backend/users/handlers.go b/backend/users/handlers.go index f57b4c7..f75f382 100644 --- a/backend/users/handlers.go +++ b/backend/users/handlers.go @@ -64,7 +64,7 @@ func LoginPOSTHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error return err } - token, err := e.Auth.Login(data.Username, data.Password) + token, err := e.Auth.Login(r, data.Username, data.Password) if err != nil { if err == auth.ErrInvalidPassword || err == ErrUnknownUser { return e.RenderError(w, fmt.Errorf("Error invalid user or password"))