From ed437c3a3f51079d4b03bb88794088f8e0214792 Mon Sep 17 00:00:00 2001 From: Isaque Veras <46972789+isaqueveras@users.noreply.github.com> Date: Sun, 1 Oct 2023 23:42:41 -0300 Subject: [PATCH] feat: geo location by ip (#144) --- app.json | 4 +- config/model.go | 2 + geoip/geoip.go | 46 +++++++++++++++++++ go.mod | 6 ++- go.sum | 11 +++-- .../persistencie/auth/postgres/session.go | 6 ++- main.go | 6 +++ ...0220812140006_create_table_sessions.up.sql | 1 + 8 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 geoip/geoip.go diff --git a/app.json b/app.json index 3ab0c5a..7a443fe 100644 --- a/app.json +++ b/app.json @@ -21,7 +21,9 @@ "access_log_directory": "/var/log/powersso/access.log", "error_log_directory": "/var/log/powersso/error.log", "permission_base": "github.com/isaqueveras/power-sso", - "access_control_allow_origin": "*" + "access_control_allow_origin": "*", + "geoip_database": "/etc/powersso/GeoLite2-City.mmdb", + "geoip_location": ["en"] }, "database": { "host": "localhost", diff --git a/config/model.go b/config/model.go index eef1021..cb21631 100644 --- a/config/model.go +++ b/config/model.go @@ -46,6 +46,8 @@ type ServerConfig struct { ErrorLogDirectory string `json:"error_log_directory"` PermissionBase string `json:"permission_base"` AccessControlAllowOrigin string `json:"access_control_allow_origin"` + GeoIPDatabase string `json:"geoip_database"` + GeoIPLocation string `json:"geoip_location"` SSL bool `json:"ssl"` CSRF bool `json:"srf"` Debug bool `json:"debug"` diff --git a/geoip/geoip.go b/geoip/geoip.go new file mode 100644 index 0000000..e91f3c4 --- /dev/null +++ b/geoip/geoip.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 Isaque Veras +// Use of this source code is governed by MIT +// license that can be found in the LICENSE file. + +package geoip + +import ( + "net" + + "github.com/isaqueveras/powersso/config" + "github.com/oschwald/geoip2-golang" +) + +var db *geoip2.Reader + +func Load() (err error) { + db, err = geoip2.Open(config.Get().Server.GeoIPDatabase) + return +} + +func Close() { _ = db.Close() } + +func Get(ip net.IP) (l string) { + if db == nil { + return "Location unavailable" + } + + city, err := db.City(ip) + if err != nil { + return "Undefined location" + } + + language := config.Get().Server.GeoIPLocation + l = city.City.Names[language] + + if len(city.Subdivisions) > 0 { + for i := range city.Subdivisions { + if len(city.Subdivisions[i].Names) > 0 && (city.Subdivisions[i].Names[language] != "") { + l += " - " + city.Subdivisions[i].Names[language] + } + } + } + + l += ", " + city.Country.Names[language] + return +} diff --git a/go.mod b/go.mod index 109327a..c4c8261 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,9 @@ require ( github.com/isaqueveras/lingo v0.0.0-20181220065520-bfdb55fa4143 github.com/jackc/pgx v3.6.2+incompatible github.com/microcosm-cc/bluemonday v1.0.22 + github.com/oschwald/geoip2-golang v1.9.0 github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.4 github.com/swaggo/files v1.0.0 github.com/swaggo/gin-swagger v1.5.3 github.com/swaggo/swag v1.8.10 @@ -60,6 +61,7 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oschwald/maxminddb-golang v1.11.0 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -69,7 +71,7 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/net v0.6.0 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.7.0 // indirect golang.org/x/tools v0.6.0 // indirect google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // indirect diff --git a/go.sum b/go.sum index 93f8dc1..e5b08a3 100644 --- a/go.sum +++ b/go.sum @@ -159,6 +159,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= +github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= +github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0= +github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg= github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= @@ -195,8 +199,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= github.com/swaggo/files v1.0.0 h1:1gGXVIeUFCS/dta17rnP0iOpr6CXFwKD7EO5ID233e4= github.com/swaggo/files v1.0.0/go.mod h1:N59U6URJLyU1PQgFqPM7wXLMhJx7QAolnvfQkqO13kc= @@ -298,8 +303,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= diff --git a/infrastructure/persistencie/auth/postgres/session.go b/infrastructure/persistencie/auth/postgres/session.go index 1bebe17..bedb730 100644 --- a/infrastructure/persistencie/auth/postgres/session.go +++ b/infrastructure/persistencie/auth/postgres/session.go @@ -6,10 +6,12 @@ package postgres import ( "database/sql" + "net" "github.com/Masterminds/squirrel" "github.com/google/uuid" "github.com/isaqueveras/powersso/database/postgres" + "github.com/isaqueveras/powersso/geoip" "github.com/isaqueveras/powersso/oops" ) @@ -22,8 +24,8 @@ type PGSession struct { func (pg *PGSession) Create(userID *uuid.UUID, clientIP, userAgent *string) (sessionID *uuid.UUID, err error) { if err = pg.DB.Builder. Insert("sessions"). - Columns("user_id", "expires_at", "ip", "user_agent"). - Values(userID, squirrel.Expr("NOW() + '15 minutes'"), clientIP, userAgent). + Columns("user_id", "expires_at", "ip", "location", "user_agent"). + Values(userID, squirrel.Expr("NOW() + '15 minutes'"), clientIP, geoip.Get(net.IP(*clientIP)), userAgent). Suffix(`RETURNING "id"`). Scan(&sessionID); err != nil { return nil, oops.Err(err) diff --git a/main.go b/main.go index 06f0664..2ec9b74 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "github.com/isaqueveras/powersso/config" "github.com/isaqueveras/powersso/database/postgres" _ "github.com/isaqueveras/powersso/docs" + "github.com/isaqueveras/powersso/geoip" "github.com/isaqueveras/powersso/scripts" "github.com/isaqueveras/powersso/server" "github.com/isaqueveras/powersso/utils" @@ -58,6 +59,11 @@ func main() { logg.Fatal("Error while serving the server GRPC: ", err) } + if err := geoip.Load(); err != nil { + logg.Warn("Not possible to load GeoIP database: ", err) + } + defer geoip.Close() + if err := group.Wait(); err != nil { logg.Fatal("Error while serving the servers: ", err) } diff --git a/migrations/20220812140006_create_table_sessions.up.sql b/migrations/20220812140006_create_table_sessions.up.sql index e789d98..7f9be6d 100644 --- a/migrations/20220812140006_create_table_sessions.up.sql +++ b/migrations/20220812140006_create_table_sessions.up.sql @@ -6,6 +6,7 @@ CREATE TABLE "sessions" ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users (id), ip VARCHAR NOT NULL, + "location" VARCHAR, user_agent VARCHAR NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,