Add user IP to its token

This commit is contained in:
Grégoire Delattre 2018-02-20 23:22:32 +01:00
parent 5e81b17e28
commit bd20a87548
5 changed files with 74 additions and 11 deletions

View File

@ -71,7 +71,7 @@ func (a *Authorizer) GenHash(password string) (string, error) {
} }
// Login cheks password and creates a jwt token // 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) u, err := a.Backend.GetUser(username)
if err != nil { if err != nil {
return nil, err return nil, err
@ -104,6 +104,7 @@ func (a *Authorizer) Login(username, password string) (*tokens.Token, error) {
t := &tokens.Token{ t := &tokens.Token{
Token: ss, Token: ss,
Username: u.GetName(), Username: u.GetName(),
IP: getIPFromRequest(r),
} }
if err := t.Add(a.db); err != nil { 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.UserAgent = req.UserAgent()
token.IP = getIPFromRequest(req)
if err := token.Update(a.db); err != nil { if err := token.Update(a.db); err != nil {
return nil, err return nil, err
} }

View File

@ -2,19 +2,21 @@ package auth
import ( import (
"context" "context"
"net"
"net/http" "net/http"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type ipContextKey string
type authContextKey string
// Middleware get User from session and put it in context // Middleware get User from session and put it in context
type Middleware struct { type Middleware struct {
authorizer *Authorizer authorizer *Authorizer
log *logrus.Entry log *logrus.Entry
} }
type authContextKey string
// NewMiddleware returns a new authentication middleware // NewMiddleware returns a new authentication middleware
func NewMiddleware(authorizer *Authorizer, log *logrus.Entry) *Middleware { func NewMiddleware(authorizer *Authorizer, log *logrus.Entry) *Middleware {
return &Middleware{ return &Middleware{
@ -101,3 +103,57 @@ func GetCurrentUser(r *http.Request, log *logrus.Entry) User {
} }
return 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)
}

View File

@ -89,6 +89,8 @@ func main() {
defer c.Stop() defer c.Stop()
n := negroni.Classic() n := negroni.Classic()
// Middleware for setting ips
n.Use(auth.NewIPMiddleware(log))
// Middleware for authentication // Middleware for authentication
n.Use(authMiddleware) n.Use(authMiddleware)
// Serve static files // Serve static files

View File

@ -4,18 +4,19 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"time"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"gitlab.quimbo.fr/odwrtw/canape/backend/sqly" "gitlab.quimbo.fr/odwrtw/canape/backend/sqly"
) )
const ( 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;` getTokenQuery = `SELECT * FROM tokens WHERE token=$1;`
getUserTokenQuery = `SELECT * FROM tokens WHERE username=$1 and token=$2;` getUserTokenQuery = `SELECT * FROM tokens WHERE username=$1 and token=$2;`
getUserTokensQuery = `SELECT * FROM tokens WHERE username=$1;` getUserTokensQuery = `SELECT * FROM tokens WHERE username=$1;`
deleteTokenQuery = `DELETE FROM tokens WHERE username=$1 AND token=$2;` 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 // Custom errors
@ -26,15 +27,17 @@ var (
// Token represents a token // Token represents a token
type Token struct { type Token struct {
sqly.BaseModel sqly.BaseModel
Username string `db:"username" json:"username"` Username string `db:"username" json:"username"`
Token string `db:"token" json:"token"` Token string `db:"token" json:"token"`
Description string `db:"description" json:"description"` Description string `db:"description" json:"description"`
UserAgent string `db:"user_agent" json:"user_agent"` 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 // Add a token to the database
func (t *Token) Add(db *sqlx.DB) error { 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 { if err != nil {
return err return err
} }

View File

@ -64,7 +64,7 @@ func LoginPOSTHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error
return err 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 != nil {
if err == auth.ErrInvalidPassword || err == ErrUnknownUser { if err == auth.ErrInvalidPassword || err == ErrUnknownUser {
return e.RenderError(w, fmt.Errorf("Error invalid user or password")) return e.RenderError(w, fmt.Errorf("Error invalid user or password"))