mirror of https://github.com/mpolden/echoip
Merge pull request #9 from levelsoftware/feature/adding-jwt-token-validation
Feature/adding jwt token validation
This commit is contained in:
commit
72da6dc5e9
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,5 +1,18 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.2.0 (2023-10-06)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- JWT Authentication
|
||||||
|
|
||||||
|
## 1.1.0 (2023-10-06)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Environment Variable Configuration [712e216](https://github.com/levelsoftware/echoip/commit/712e2166d51fdb85229f52caa380743245f31dfa)
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0 (2023-10-06)
|
## 1.0.0 (2023-10-06)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
30
README.md
30
README.md
|
@ -55,8 +55,7 @@ $ curl -H 'Accept: application/json' ip.level.io # or curl ip.level.io/json
|
||||||
"ip_decimal": 2130706433,
|
"ip_decimal": 2130706433,
|
||||||
"asn": "AS59795",
|
"asn": "AS59795",
|
||||||
"asn_org": "Hosting4Real"
|
"asn_org": "Hosting4Real"
|
||||||
}
|
} ```
|
||||||
```
|
|
||||||
|
|
||||||
Port testing:
|
Port testing:
|
||||||
|
|
||||||
|
@ -85,6 +84,7 @@ between IPv4 and IPv6 lookup.
|
||||||
- All endpoints (except `/port`) can return information about a custom IP address specified via `?ip=` query parameter
|
- All endpoints (except `/port`) can return information about a custom IP address specified via `?ip=` query parameter
|
||||||
- Open source under the [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause)
|
- Open source under the [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause)
|
||||||
- Supports IP Stack API or GeoIP
|
- Supports IP Stack API or GeoIP
|
||||||
|
- JWT Authentication
|
||||||
|
|
||||||
### Installation from Release
|
### Installation from Release
|
||||||
|
|
||||||
|
@ -121,6 +121,10 @@ Database = "ipstack" # use "IP Stack" or "GeoIP"
|
||||||
TrustedHeaders = [] # Which header to trust, eg, `["X-Real-IP"]`
|
TrustedHeaders = [] # Which header to trust, eg, `["X-Real-IP"]`
|
||||||
Profile = false # enable debug / profiling
|
Profile = false # enable debug / profiling
|
||||||
|
|
||||||
|
[Jwt]
|
||||||
|
Enabled = false
|
||||||
|
Secret = ""
|
||||||
|
|
||||||
[IPStack]
|
[IPStack]
|
||||||
ApiKey = ""
|
ApiKey = ""
|
||||||
UseHttps = true
|
UseHttps = true
|
||||||
|
@ -133,7 +137,7 @@ AsnFile = ""
|
||||||
```
|
```
|
||||||
|
|
||||||
### Environment Variables for Configuration
|
### Environment Variables for Configuration
|
||||||
You can also use environment variables for configuration, most likely used for Docker.
|
You can also use environment variables for configuration, most likely used for Docker. Configuration file takes precedence first, and then environment variables. Remove the value from the config file if you wish to use the environment variable.
|
||||||
|
|
||||||
```
|
```
|
||||||
ECHOIP_LISTEN=":8080"
|
ECHOIP_LISTEN=":8080"
|
||||||
|
@ -152,8 +156,28 @@ ECHOIP_SHOW_SPONSOR=true
|
||||||
ECHOIP_PROFILE=false
|
ECHOIP_PROFILE=false
|
||||||
ECHOIP_IPSTACK_USE_HTTPS=true
|
ECHOIP_IPSTACK_USE_HTTPS=true
|
||||||
ECHOIP_IPSTACK_ENABLE_SECURITY=true
|
ECHOIP_IPSTACK_ENABLE_SECURITY=true
|
||||||
|
ECHOIP_JWT_AUTH=false
|
||||||
|
ECHOIP_JWT_SIGNING_METHOD=HS256
|
||||||
|
ECHOIP_JWT_SECRET="HS256"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Authenticate each API request with JWT
|
||||||
|
|
||||||
|
You can authenticate each API request with JWT token.
|
||||||
|
Just enable `config.Jwt.Enabled` and add your JWT secret to `config.Jwt.Secret`.
|
||||||
|
|
||||||
|
EchoIP validates JWT signing algorithm, `config.SigningMethod` should be one of available from `golang-jwt/jwt` and match your expceted algorithm. Currently only supporting HMAC signing.
|
||||||
|
|
||||||
|
`config.SigningMethod string`
|
||||||
|
|
||||||
|
```
|
||||||
|
# HS256 | HS384 | HS512
|
||||||
|
```
|
||||||
|
|
||||||
|
Requests will be accepted if a valid token is provided in `Authorization: Bearer $token` header.
|
||||||
|
|
||||||
|
A `401` will be returned should the token not be valid.
|
||||||
|
|
||||||
### Caching with Redis
|
### Caching with Redis
|
||||||
|
|
||||||
You can connect EchoIP to a Redis client to cache each request per IP. You can configure the life of the key in `config.CacheTtl`.
|
You can connect EchoIP to a Redis client to cache each request per IP. You can configure the life of the key in `config.CacheTtl`.
|
||||||
|
|
|
@ -102,14 +102,20 @@ func main() {
|
||||||
serverCache = &cache.Null{}
|
serverCache = &cache.Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
server := http.New(parser, serverCache, runConfig.CacheTtl, runConfig.Profile)
|
server := http.New(parser, serverCache, &runConfig)
|
||||||
server.IPHeaders = runConfig.TrustedHeaders
|
server.IPHeaders = runConfig.TrustedHeaders
|
||||||
|
|
||||||
if _, err := os.Stat(runConfig.TemplateDir); err == nil {
|
if _, err := os.Stat(runConfig.TemplateDir); err != nil {
|
||||||
server.Template = runConfig.TemplateDir
|
runConfig.TemplateDir = ""
|
||||||
} else {
|
|
||||||
log.Printf("Not configuring default handler: Template not found: %s", runConfig.TemplateDir)
|
log.Printf("Not configuring default handler: Template not found: %s", runConfig.TemplateDir)
|
||||||
}
|
}
|
||||||
|
if runConfig.Jwt.Enabled {
|
||||||
|
log.Println("Enabling JWT Auth")
|
||||||
|
|
||||||
|
if len(runConfig.Jwt.Secret) == 0 {
|
||||||
|
log.Fatal("Please provide a JWT Token secret when JWT is enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
if runConfig.ReverseLookup {
|
if runConfig.ReverseLookup {
|
||||||
log.Println("Enabling reverse lookup")
|
log.Println("Enabling reverse lookup")
|
||||||
server.LookupAddr = iputil.LookupAddr
|
server.LookupAddr = iputil.LookupAddr
|
||||||
|
@ -118,9 +124,9 @@ func main() {
|
||||||
log.Println("Enabling port lookup")
|
log.Println("Enabling port lookup")
|
||||||
server.LookupPort = iputil.LookupPort
|
server.LookupPort = iputil.LookupPort
|
||||||
}
|
}
|
||||||
|
|
||||||
if runConfig.ShowSponsor {
|
if runConfig.ShowSponsor {
|
||||||
log.Println("Enabling sponsor logo")
|
log.Println("Enabling sponsor logo")
|
||||||
server.Sponsor = runConfig.ShowSponsor
|
|
||||||
}
|
}
|
||||||
if len(runConfig.TrustedHeaders) > 0 {
|
if len(runConfig.TrustedHeaders) > 0 {
|
||||||
log.Printf("Trusting remote IP from header(s): %s", runConfig.TrustedHeaders)
|
log.Printf("Trusting remote IP from header(s): %s", runConfig.TrustedHeaders)
|
||||||
|
|
|
@ -18,6 +18,12 @@ type GeoIP struct {
|
||||||
AsnFile string
|
AsnFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Jwt struct {
|
||||||
|
Enabled bool
|
||||||
|
SigningMethod string
|
||||||
|
Secret string
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Listen string
|
Listen string
|
||||||
TemplateDir string
|
TemplateDir string
|
||||||
|
@ -30,9 +36,11 @@ type Config struct {
|
||||||
|
|
||||||
Database string
|
Database string
|
||||||
Profile bool
|
Profile bool
|
||||||
|
Debug bool
|
||||||
|
|
||||||
IPStack IPStack
|
IPStack IPStack
|
||||||
GeoIP GeoIP
|
GeoIP GeoIP
|
||||||
|
Jwt Jwt
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfig() (Config, error) {
|
func GetConfig() (Config, error) {
|
||||||
|
@ -41,6 +49,10 @@ func GetConfig() (Config, error) {
|
||||||
TemplateDir: getenv_string("ECHOIP_TEMPLATE_DIR", "html/"),
|
TemplateDir: getenv_string("ECHOIP_TEMPLATE_DIR", "html/"),
|
||||||
RedisUrl: getenv_string("ECHOIP_REDIS_URL", ""),
|
RedisUrl: getenv_string("ECHOIP_REDIS_URL", ""),
|
||||||
Database: getenv_string("ECHOIP_DATABASE", "geoip"),
|
Database: getenv_string("ECHOIP_DATABASE", "geoip"),
|
||||||
|
Jwt: Jwt{
|
||||||
|
Secret: getenv_string("ECHOIP_JWT_SECRET", ""),
|
||||||
|
SigningMethod: getenv_string("ECHOIP_JWT_SIGNING_METHOD", "HS256"),
|
||||||
|
},
|
||||||
IPStack: IPStack{
|
IPStack: IPStack{
|
||||||
ApiKey: getenv_string("ECHOIP_IPSTACK_API_KEY", ""),
|
ApiKey: getenv_string("ECHOIP_IPSTACK_API_KEY", ""),
|
||||||
},
|
},
|
||||||
|
@ -51,6 +63,18 @@ func GetConfig() (Config, error) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jwtAuthEnabled, err := getenv_bool("ECHOIP_JWT_AUTH", false)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
defaultConfig.Jwt.Enabled = jwtAuthEnabled
|
||||||
|
|
||||||
|
debug, err := getenv_bool("ECHOIP_DEBUG", false)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
defaultConfig.Debug = debug
|
||||||
|
|
||||||
cacheTtl, err := getenv_int("ECHOIP_CACHE_TTL", 3600)
|
cacheTtl, err := getenv_int("ECHOIP_CACHE_TTL", 3600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Config{}, err
|
return Config{}, err
|
||||||
|
|
|
@ -7,7 +7,13 @@ PortLookup = true
|
||||||
ShowSponsor = true
|
ShowSponsor = true
|
||||||
Database = "ipstack" # use "IP Stack" or "GeoIP"
|
Database = "ipstack" # use "IP Stack" or "GeoIP"
|
||||||
TrustedHeaders = []
|
TrustedHeaders = []
|
||||||
Profile = false # enable debug / profiling
|
Profile = false # enable profiling
|
||||||
|
Debug = false # enable debugging, ex print jwt token errors
|
||||||
|
|
||||||
|
[Jwt]
|
||||||
|
Enabled = false
|
||||||
|
SigningMethod = "HS256" # HS256 | HS384 | HS512
|
||||||
|
Secret = ""
|
||||||
|
|
||||||
[IPStack]
|
[IPStack]
|
||||||
ApiKey = "HelloWorld"
|
ApiKey = "HelloWorld"
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -5,6 +5,7 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.3.2
|
github.com/BurntSushi/toml v1.3.2
|
||||||
github.com/go-redis/cache/v9 v9.0.0
|
github.com/go-redis/cache/v9 v9.0.0
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/oschwald/geoip2-golang v1.5.0
|
github.com/oschwald/geoip2-golang v1.5.0
|
||||||
github.com/qioalice/ipstack v1.0.1
|
github.com/qioalice/ipstack v1.0.1
|
||||||
github.com/redis/go-redis/v9 v9.2.1
|
github.com/redis/go-redis/v9 v9.2.1
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -21,6 +21,8 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
|
||||||
github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0=
|
github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0=
|
||||||
github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI=
|
github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import "net/http"
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
type appError struct {
|
type appError struct {
|
||||||
Error error
|
Error error
|
||||||
|
@ -22,6 +25,11 @@ func notFound(err error) *appError {
|
||||||
}
|
}
|
||||||
|
|
||||||
func badRequest(err error) *appError {
|
func badRequest(err error) *appError {
|
||||||
|
badAuth := new(InvalidTokenError)
|
||||||
|
if errors.As(err, &badAuth) {
|
||||||
|
return &appError{Error: err, Code: http.StatusUnauthorized}
|
||||||
|
}
|
||||||
|
|
||||||
return &appError{Error: err, Code: http.StatusBadRequest}
|
return &appError{Error: err, Code: http.StatusBadRequest}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
66
http/http.go
66
http/http.go
|
@ -7,12 +7,15 @@ import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
|
|
||||||
rcache "github.com/go-redis/cache/v9"
|
rcache "github.com/go-redis/cache/v9"
|
||||||
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/levelsoftware/echoip/cache"
|
"github.com/levelsoftware/echoip/cache"
|
||||||
|
"github.com/levelsoftware/echoip/config"
|
||||||
parser "github.com/levelsoftware/echoip/iputil/paser"
|
parser "github.com/levelsoftware/echoip/iputil/paser"
|
||||||
"github.com/levelsoftware/echoip/useragent"
|
"github.com/levelsoftware/echoip/useragent"
|
||||||
|
|
||||||
|
@ -27,15 +30,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Template string
|
|
||||||
IPHeaders []string
|
IPHeaders []string
|
||||||
LookupAddr func(net.IP) (string, error)
|
LookupAddr func(net.IP) (string, error)
|
||||||
LookupPort func(net.IP, uint64) error
|
LookupPort func(net.IP, uint64) error
|
||||||
cache cache.Cache
|
cache cache.Cache
|
||||||
cacheTtl int
|
runConfig *config.Config
|
||||||
parser parser.Parser
|
parser parser.Parser
|
||||||
profile bool
|
|
||||||
Sponsor bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortResponse struct {
|
type PortResponse struct {
|
||||||
|
@ -44,8 +44,8 @@ type PortResponse struct {
|
||||||
Reachable bool `json:"reachable"`
|
Reachable bool `json:"reachable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(parser parser.Parser, cache cache.Cache, cacheTtl int, profile bool) *Server {
|
func New(parser parser.Parser, cache cache.Cache, runConfig *config.Config) *Server {
|
||||||
return &Server{cache: cache, cacheTtl: cacheTtl, parser: parser, profile: profile}
|
return &Server{cache: cache, parser: parser, runConfig: runConfig}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipFromForwardedForHeader(v string) string {
|
func ipFromForwardedForHeader(v string) string {
|
||||||
|
@ -104,6 +104,10 @@ func userAgentFromRequest(r *http.Request) *useragent.UserAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) newResponse(r *http.Request) (parser.Response, error) {
|
func (s *Server) newResponse(r *http.Request) (parser.Response, error) {
|
||||||
|
if err := handleAuth(r, s.runConfig); err != nil {
|
||||||
|
return parser.Response{}, err
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
ip, err := ipFromRequest(s.IPHeaders, r, true)
|
ip, err := ipFromRequest(s.IPHeaders, r, true)
|
||||||
|
@ -130,7 +134,7 @@ func (s *Server) newResponse(r *http.Request) (parser.Response, error) {
|
||||||
response, err = s.parser.Parse(ip, hostname)
|
response, err = s.parser.Parse(ip, hostname)
|
||||||
|
|
||||||
log.Printf("Caching response for %s", ip.String())
|
log.Printf("Caching response for %s", ip.String())
|
||||||
if err := s.cache.Set(ctx, ip.String(), cachedResponse.Build(response), s.cacheTtl); err != nil {
|
if err := s.cache.Set(ctx, ip.String(), cachedResponse.Build(response), s.runConfig.CacheTtl); err != nil {
|
||||||
return parser.Response{}, err
|
return parser.Response{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,6 +162,10 @@ func (s *Server) newPortResponse(r *http.Request) (PortResponse, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) CLIHandler(w http.ResponseWriter, r *http.Request) *appError {
|
func (s *Server) CLIHandler(w http.ResponseWriter, r *http.Request) *appError {
|
||||||
|
if err := handleAuth(r, s.runConfig); err != nil {
|
||||||
|
return badRequest(err).WithMessage(err.Error()).AsJSON()
|
||||||
|
}
|
||||||
|
|
||||||
ip, err := ipFromRequest(s.IPHeaders, r, true)
|
ip, err := ipFromRequest(s.IPHeaders, r, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return badRequest(err).WithMessage(err.Error()).AsJSON()
|
return badRequest(err).WithMessage(err.Error()).AsJSON()
|
||||||
|
@ -259,10 +267,12 @@ func (s *Server) DefaultHandler(w http.ResponseWriter, r *http.Request) *appErro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return badRequest(err).WithMessage(err.Error())
|
return badRequest(err).WithMessage(err.Error())
|
||||||
}
|
}
|
||||||
t, err := template.ParseGlob(s.Template + "/*")
|
|
||||||
|
t, err := template.ParseGlob(s.runConfig.TemplateDir + "/*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return internalServerError(err)
|
return internalServerError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
json, err := json.MarshalIndent(response, "", " ")
|
json, err := json.MarshalIndent(response, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return internalServerError(err)
|
return internalServerError(err)
|
||||||
|
@ -287,7 +297,7 @@ func (s *Server) DefaultHandler(w http.ResponseWriter, r *http.Request) *appErro
|
||||||
response.Longitude + 0.05,
|
response.Longitude + 0.05,
|
||||||
string(json),
|
string(json),
|
||||||
s.LookupPort != nil,
|
s.LookupPort != nil,
|
||||||
s.Sponsor,
|
s.runConfig.ShowSponsor,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := t.Execute(w, &data); err != nil {
|
if err := t.Execute(w, &data); err != nil {
|
||||||
|
@ -373,7 +383,7 @@ func (s *Server) Handler() http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Browser
|
// Browser
|
||||||
if s.Template != "" {
|
if s.runConfig.TemplateDir != "" {
|
||||||
r.Route("GET", "/", s.DefaultHandler)
|
r.Route("GET", "/", s.DefaultHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +393,7 @@ func (s *Server) Handler() http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Profiling
|
// Profiling
|
||||||
if s.profile {
|
if s.runConfig.Profile {
|
||||||
r.Route("GET", "/debug/pprof/cmdline", wrapHandlerFunc(pprof.Cmdline))
|
r.Route("GET", "/debug/pprof/cmdline", wrapHandlerFunc(pprof.Cmdline))
|
||||||
r.Route("GET", "/debug/pprof/profile", wrapHandlerFunc(pprof.Profile))
|
r.Route("GET", "/debug/pprof/profile", wrapHandlerFunc(pprof.Profile))
|
||||||
r.Route("GET", "/debug/pprof/symbol", wrapHandlerFunc(pprof.Symbol))
|
r.Route("GET", "/debug/pprof/symbol", wrapHandlerFunc(pprof.Symbol))
|
||||||
|
@ -401,3 +411,37 @@ func (s *Server) ListenAndServe(addr string) error {
|
||||||
func formatCoordinate(c float64) string {
|
func formatCoordinate(c float64) string {
|
||||||
return strconv.FormatFloat(c, 'f', 6, 64)
|
return strconv.FormatFloat(c, 'f', 6, 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InvalidTokenError struct{}
|
||||||
|
|
||||||
|
func (m *InvalidTokenError) Error() string {
|
||||||
|
return "invalid_token"
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleAuth(r *http.Request, runConfig *config.Config) error {
|
||||||
|
if !runConfig.Jwt.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
authorization := r.Header.Get("Authorization")
|
||||||
|
tokenString := strings.ReplaceAll(authorization, "Bearer ", "")
|
||||||
|
|
||||||
|
if _, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
expected := reflect.TypeOf(jwt.GetSigningMethod(runConfig.Jwt.SigningMethod))
|
||||||
|
got := reflect.TypeOf(token.Method)
|
||||||
|
if expected != got {
|
||||||
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only support SigningMethodHMAC ( Others will be quite a bit more complicated )
|
||||||
|
return []byte(runConfig.Jwt.Secret), nil
|
||||||
|
}); err != nil {
|
||||||
|
if runConfig.Debug {
|
||||||
|
log.Printf("Error validating token ( %s ): %s \n", tokenString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(InvalidTokenError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/levelsoftware/echoip/cache"
|
"github.com/levelsoftware/echoip/cache"
|
||||||
|
"github.com/levelsoftware/echoip/config"
|
||||||
"github.com/levelsoftware/echoip/iputil"
|
"github.com/levelsoftware/echoip/iputil"
|
||||||
"github.com/levelsoftware/echoip/iputil/geo"
|
"github.com/levelsoftware/echoip/iputil/geo"
|
||||||
parser "github.com/levelsoftware/echoip/iputil/paser"
|
parser "github.com/levelsoftware/echoip/iputil/paser"
|
||||||
|
@ -84,7 +85,13 @@ func (fc *FakeCache) Set(ctx context.Context, ip string, response cache.CachedRe
|
||||||
|
|
||||||
func testServer() *Server {
|
func testServer() *Server {
|
||||||
fakeCache := FakeCache{}
|
fakeCache := FakeCache{}
|
||||||
return &Server{cache: &fakeCache, cacheTtl: 100, parser: &testDb{}, LookupAddr: lookupAddr, LookupPort: lookupPort}
|
return &Server{
|
||||||
|
cache: &fakeCache,
|
||||||
|
parser: &testDb{},
|
||||||
|
LookupAddr: lookupAddr,
|
||||||
|
LookupPort: lookupPort,
|
||||||
|
runConfig: &config.Config{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpGet(url string, acceptMediaType string, userAgent string) (string, int, error) {
|
func httpGet(url string, acceptMediaType string, userAgent string) (string, int, error) {
|
||||||
|
|
Loading…
Reference in New Issue