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
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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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
}

View File

@ -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"))