Geo-gating on signup

This commit is contained in:
Noah Petherbridge 2023-06-24 15:39:45 -07:00
parent 45cb4d260e
commit c172565a56
7 changed files with 170 additions and 2 deletions

4
go.mod
View File

@ -35,6 +35,8 @@ require (
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-sqlite3 v1.14.14 // indirect github.com/mattn/go-sqlite3 v1.14.14 // indirect
github.com/microcosm-cc/bluemonday v1.0.19 // indirect github.com/microcosm-cc/bluemonday v1.0.19 // indirect
github.com/oschwald/geoip2-golang v1.9.0 // indirect
github.com/oschwald/maxminddb-golang v1.11.0 // indirect
github.com/russross/blackfriday v1.5.2 // indirect github.com/russross/blackfriday v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
@ -50,7 +52,7 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect golang.org/x/sys v0.9.0 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.12 // indirect golang.org/x/tools v0.1.12 // indirect

6
go.sum
View File

@ -109,6 +109,10 @@ github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLv
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c=
github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
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/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -214,6 +218,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+R
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

20
pkg/config/geo_gate.go Normal file
View File

@ -0,0 +1,20 @@
package config
// GeoIP gating rules. TODO: make dynamically configurable.
// GeoIP database path (standard location on Fedora/Debian)
const GeoIPPath = "/usr/share/GeoIP/GeoLite2-City.mmdb"
// US states to block.
var BlockUSStates = map[string]interface{}{
"UT": nil, // Utah
"LA": nil, // Louisiana
"MS": nil, // Mississippi
"AR": nil, // Arkansas
"MT": nil, // Montana
}
// Countries to block.
var BlockCountries = map[string]interface{}{
// "DE": nil,
}

View File

@ -0,0 +1,88 @@
package middleware
import (
"fmt"
"net"
"net/http"
"strings"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/controller/index"
"code.nonshy.com/nonshy/website/pkg/log"
"code.nonshy.com/nonshy/website/pkg/session"
"code.nonshy.com/nonshy/website/pkg/utility"
"github.com/oschwald/geoip2-golang"
)
// GeoGate: block access to the site based on the user's location (due to local laws or regulations).
func GeoGate(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Flash errors to admins.
onError := func(err error) {
session.FlashError(w, r, "GeoIP: %s", err)
handler.ServeHTTP(w, r)
}
// See where they're coming from.
db, err := geoip2.Open(config.GeoIPPath)
if err != nil {
onError(err)
return
}
defer db.Close()
// If you are using strings that may be invalid, check that ip is not nil
addr := strings.SplitN(utility.IPAddress(r), ":", 2)[0]
ip := net.ParseIP(utility.IPAddress(r))
log.Info("IP addr: %s (raw: %s)", ip, addr)
if ip != nil {
record, err := db.City(ip)
if err != nil {
onError(err)
return
}
log.Info("Raw: %+v", record)
// Blocked by US states
if record.Country.IsoCode == "US" {
for _, sub := range record.Subdivisions {
if _, ok := config.BlockUSStates[sub.IsoCode]; ok {
session.LogoutUser(w, r)
page := index.StaticTemplate("errors/geo_gate.html")()
page(w, r)
return
}
}
}
// Blocked by country code
if _, ok := config.BlockCountries[record.Country.IsoCode]; ok {
session.LogoutUser(w, r)
page := index.StaticTemplate("errors/geo_gate.html")()
page(w, r)
return
}
// Debug info
fmt.Printf("Portuguese (BR) city name: %v\n", record.City.Names["pt-BR"])
if len(record.Subdivisions) > 0 {
fmt.Printf("English subdivision name: %v\n", record.Subdivisions[0].Names["en"])
}
fmt.Printf("Russian country name: %v\n", record.Country.Names["ru"])
fmt.Printf("ISO country code: %v\n", record.Country.IsoCode)
fmt.Printf("Time zone: %v\n", record.Location.TimeZone)
fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
// Output:
// Portuguese (BR) city name: Londres
// English subdivision name: England
// Russian country name: Великобритания
// ISO country code: GB
// Time zone: Europe/London
// Coordinates: 51.5142, -0.0931
}
handler.ServeHTTP(w, r)
})
}

View File

@ -33,10 +33,11 @@ func New() http.Handler {
mux.HandleFunc("/contact", index.Contact()) mux.HandleFunc("/contact", index.Contact())
mux.HandleFunc("/login", account.Login()) mux.HandleFunc("/login", account.Login())
mux.HandleFunc("/logout", account.Logout()) mux.HandleFunc("/logout", account.Logout())
mux.HandleFunc("/signup", account.Signup()) mux.Handle("/signup", middleware.GeoGate(account.Signup()))
mux.HandleFunc("/forgot-password", account.ForgotPassword()) mux.HandleFunc("/forgot-password", account.ForgotPassword())
mux.HandleFunc("/settings/confirm-email", account.ConfirmEmailChange()) mux.HandleFunc("/settings/confirm-email", account.ConfirmEmailChange())
mux.HandleFunc("/markdown", index.StaticTemplate("markdown.html")()) mux.HandleFunc("/markdown", index.StaticTemplate("markdown.html")())
mux.HandleFunc("/test/geo-gate", index.StaticTemplate("errors/geo_gate.html")())
// Login Required. Pages that non-certified users can access. // Login Required. Pages that non-certified users can access.
mux.Handle("/me", middleware.LoginRequired(account.Dashboard())) mux.Handle("/me", middleware.LoginRequired(account.Dashboard()))

23
pkg/utility/ip_address.go Normal file
View File

@ -0,0 +1,23 @@
package utility
import (
"net/http"
"strings"
"code.nonshy.com/nonshy/website/pkg/config"
)
/*
IPAddress returns the best guess at the user's IP address, as a string for logging.
*/
func IPAddress(r *http.Request) string {
if config.Current.UseXForwardedFor {
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
return realIP
}
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
return strings.SplitN(xff, " ", 1)[0]
}
}
return r.RemoteAddr
}

View File

@ -0,0 +1,28 @@
{{define "content"}}
<div class="container">
<section class="hero block is-warning is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">Not Available</h1>
</div>
</div>
</section>
<div class="block content p-4 mb-0">
<h1>{{PrettyTitle}} is not available in your area</h1>
<p>
We regret to inform you that {{PrettyTitle}} will not be available in your area.
This may likely be due to local laws and regulations in your country or state of
origin regarding 'adult sites' and whether they are required to ask for your
photo ID to be on file. We do not, at this time, wish to handle such sensitive
information from our users (so that we don't risk such information being hacked
or leaked, which you can probably agree is a good call).
</p>
<p>
If you already have an account with us, please <a href="/contact">contact support</a>
for next steps (e.g., in case you would like us to delete your account).
</p>
</div>
</div>
{{end}}