-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 4faf457
Showing
18 changed files
with
1,824 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# This workflow will build a golang project | ||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go | ||
|
||
name: Go | ||
|
||
on: | ||
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
branches: [ "master" ] | ||
|
||
jobs: | ||
|
||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: '1.21' | ||
|
||
- name: Build | ||
run: go build -v ./... | ||
|
||
- name: Test | ||
run: go test -v ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Access Control | ||
![Go Workflow](https://github.com/0xsequence/accesscontrol/actions/workflows/go.yml/badge.svg) | ||
[![Go Reference](https://pkg.go.dev/badge/github.com/0xsequence/accesscontrol.svg)](https://pkg.go.dev/github.com/0xsequence/accesscontrol) | ||
|
||
This package implements Session and Access Control middleware. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package accesscontrol | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/0xsequence/accesscontrol/proto" | ||
) | ||
|
||
func defaultErrHandler(r *http.Request, w http.ResponseWriter, err error) { | ||
rpcErr, ok := err.(proto.WebRPCError) | ||
if !ok { | ||
rpcErr = proto.ErrWebrpcEndpoint.WithCause(err) | ||
} | ||
|
||
w.Header().Set("Content-Type", "application/json") | ||
w.WriteHeader(rpcErr.HTTPStatus) | ||
|
||
respBody, _ := json.Marshal(rpcErr) | ||
w.Write(respBody) | ||
} | ||
|
||
type KeyFunc func(*http.Request) string | ||
|
||
type UserStore interface { | ||
GetUser(ctx context.Context, address string) (any, bool, error) | ||
} | ||
|
||
// Config is a generic map of services/methods to a config value. | ||
type Config[T any] map[string]map[string]T | ||
|
||
// Get returns the config value for the given request. | ||
func (c Config[T]) Get(r *rcpRequest) (v T, ok bool) { | ||
if c == nil || r.Package != "rpc" { | ||
return v, false | ||
} | ||
serviceCfg, ok := c[r.Service] | ||
if !ok { | ||
return v, false | ||
} | ||
methodCfg, ok := serviceCfg[r.Method] | ||
if !ok { | ||
return v, false | ||
} | ||
return methodCfg, true | ||
} | ||
|
||
// rcpRequest is a parsed RPC request. | ||
type rcpRequest struct { | ||
Package string | ||
Service string | ||
Method string | ||
} | ||
|
||
// newRequest parses a path into an rcpRequest. | ||
func newRequest(path string) *rcpRequest { | ||
parts := strings.Split(path, "/") | ||
if len(parts) != 4 { | ||
return nil | ||
} | ||
if parts[0] != "" { | ||
return nil | ||
} | ||
t := rcpRequest{ | ||
Package: parts[1], | ||
Service: parts[2], | ||
Method: parts[3], | ||
} | ||
if t.Package == "" || t.Service == "" || t.Method == "" { | ||
return nil | ||
} | ||
return &t | ||
} | ||
|
||
// ACL is a list of session types, encoded as a bitfield. | ||
// SessionType(n) is represented by n=-the bit. | ||
type ACL uint64 | ||
|
||
// NewACL returns a new ACL with the given session types. | ||
func NewACL(t ...proto.SessionType) ACL { | ||
var types ACL | ||
for _, v := range t { | ||
types = types.And(v) | ||
} | ||
return types | ||
} | ||
|
||
// And returns a new ACL with the given session types added. | ||
func (t ACL) And(types ...proto.SessionType) ACL { | ||
for _, v := range types { | ||
t |= 1 << v | ||
} | ||
return t | ||
} | ||
|
||
// Includes returns true if the ACL includes the given session type. | ||
func (t ACL) Includes(session proto.SessionType) bool { | ||
return t&ACL(1<<session) != 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package accesscontrol_test | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/0xsequence/accesscontrol/proto" | ||
"github.com/go-chi/jwtauth/v5" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func mustJWT(t *testing.T, auth *jwtauth.JWTAuth, claims map[string]any) string { | ||
t.Helper() | ||
if claims == nil { | ||
return "" | ||
} | ||
_, token, err := auth.Encode(claims) | ||
require.NoError(t, err) | ||
return token | ||
} | ||
|
||
const HeaderKey = "Test-Key" | ||
|
||
func keyFunc(r *http.Request) string { | ||
return r.Header.Get(HeaderKey) | ||
} | ||
|
||
func executeRequest(ctx context.Context, handler http.Handler, path, accessKey, jwt string) (bool, http.Header, error) { | ||
req, err := http.NewRequest("POST", path, nil) | ||
if err != nil { | ||
return false, nil, err | ||
} | ||
req.Header.Set("X-Real-IP", "127.0.0.1") | ||
if accessKey != "" { | ||
req.Header.Set(HeaderKey, accessKey) | ||
} | ||
if jwt != "" { | ||
req.Header.Set("Authorization", "Bearer "+jwt) | ||
} | ||
|
||
rr := httptest.NewRecorder() | ||
handler.ServeHTTP(rr, req.WithContext(ctx)) | ||
|
||
if status := rr.Result().StatusCode; status < http.StatusOK || status >= http.StatusBadRequest { | ||
w := proto.WebRPCError{} | ||
json.Unmarshal(rr.Body.Bytes(), &w) | ||
return false, rr.Header(), w | ||
} | ||
|
||
return true, rr.Header(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package accesscontrol | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/0xsequence/accesscontrol/proto" | ||
) | ||
|
||
type contextKey struct { | ||
name string | ||
} | ||
|
||
func (k *contextKey) String() string { | ||
return "quotacontrol context value " + k.name | ||
} | ||
|
||
var ( | ||
ctxKeySessionType = &contextKey{"SessionType"} | ||
ctxKeyAccount = &contextKey{"Account"} | ||
ctxKeyUser = &contextKey{"User"} | ||
ctxKeyService = &contextKey{"Service"} | ||
ctxKeyAccessKey = &contextKey{"AccessKey"} | ||
ctxKeyProjectID = &contextKey{"ProjectID"} | ||
) | ||
|
||
// WithSessionType adds the access key to the context. | ||
func WithSessionType(ctx context.Context, accessType proto.SessionType) context.Context { | ||
return context.WithValue(ctx, ctxKeySessionType, accessType) | ||
} | ||
|
||
// GetSessionType returns the access key from the context. | ||
func GetSessionType(ctx context.Context) (proto.SessionType, bool) { | ||
v, ok := ctx.Value(ctxKeySessionType).(proto.SessionType) | ||
if !ok { | ||
return proto.SessionType_Public, false | ||
} | ||
return v, true | ||
} | ||
|
||
// WithAccount adds the account to the context. | ||
func WithAccount(ctx context.Context, account string) context.Context { | ||
return context.WithValue(ctx, ctxKeyAccount, account) | ||
} | ||
|
||
// GetAccount returns the account from the context. | ||
func GetAccount(ctx context.Context) (string, bool) { | ||
v, ok := ctx.Value(ctxKeyAccount).(string) | ||
return v, ok | ||
} | ||
|
||
// WithUser adds the user to the context. | ||
func WithUser(ctx context.Context, user any) context.Context { | ||
return context.WithValue(ctx, ctxKeyUser, user) | ||
} | ||
|
||
// GetUser returns the user from the context. | ||
func GetUser[T any](ctx context.Context) (T, bool) { | ||
v, ok := ctx.Value(ctxKeyUser).(T) | ||
return v, ok | ||
} | ||
|
||
// WithService adds the service to the context. | ||
func WithService(ctx context.Context, service string) context.Context { | ||
return context.WithValue(ctx, ctxKeyService, service) | ||
} | ||
|
||
// GetService returns the service from the context. | ||
func GetService(ctx context.Context) (string, bool) { | ||
v, ok := ctx.Value(ctxKeyService).(string) | ||
return v, ok | ||
} | ||
|
||
// WithAccessKey adds the access key to the context. | ||
func WithAccessKey(ctx context.Context, accessKey string) context.Context { | ||
return context.WithValue(ctx, ctxKeyAccessKey, accessKey) | ||
} | ||
|
||
// GetAccessKey returns the access key from the context. | ||
func GetAccessKey(ctx context.Context) (string, bool) { | ||
v, ok := ctx.Value(ctxKeyAccessKey).(string) | ||
return v, ok | ||
} | ||
|
||
// withProjectID adds the projectID to the context. | ||
func withProjectID(ctx context.Context, projectID uint64) context.Context { | ||
return context.WithValue(ctx, ctxKeyProjectID, projectID) | ||
} | ||
|
||
// GetProjectID returns the projectID and if its active from the context. | ||
// In case its not set, it will return 0. | ||
func GetProjectID(ctx context.Context) (uint64, bool) { | ||
v, ok := ctx.Value(ctxKeyProjectID).(uint64) | ||
return v, ok | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
module github.com/0xsequence/accesscontrol | ||
|
||
go 1.23.2 | ||
|
||
require ( | ||
github.com/go-chi/chi/v5 v5.1.0 | ||
github.com/go-chi/jwtauth/v5 v5.3.1 | ||
github.com/lestrrat-go/jwx/v2 v2.1.1 | ||
github.com/stretchr/testify v1.9.0 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect | ||
github.com/goccy/go-json v0.10.3 // indirect | ||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect | ||
github.com/lestrrat-go/httpcc v1.0.1 // indirect | ||
github.com/lestrrat-go/httprc v1.0.6 // indirect | ||
github.com/lestrrat-go/iter v1.0.2 // indirect | ||
github.com/lestrrat-go/option v1.0.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/segmentio/asm v1.2.0 // indirect | ||
golang.org/x/crypto v0.25.0 // indirect | ||
golang.org/x/sys v0.22.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= | ||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= | ||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= | ||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= | ||
github.com/go-chi/jwtauth/v5 v5.3.1 h1:1ePWrjVctvp1tyBq5b/2ER8Th/+RbYc7x4qNsc5rh5A= | ||
github.com/go-chi/jwtauth/v5 v5.3.1/go.mod h1:6Fl2RRmWXs3tJYE1IQGX81FsPoGqDwq9c15j52R5q80= | ||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= | ||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= | ||
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= | ||
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= | ||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= | ||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= | ||
github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= | ||
github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= | ||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= | ||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= | ||
github.com/lestrrat-go/jwx/v2 v2.1.1 h1:Y2ltVl8J6izLYFs54BVcpXLv5msSW4o8eXwnzZLI32E= | ||
github.com/lestrrat-go/jwx/v2 v2.1.1/go.mod h1:4LvZg7oxu6Q5VJwn7Mk/UwooNRnTHUpXBj2C4j3HNx0= | ||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= | ||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= | ||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= | ||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= | ||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= | ||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
go 1.23.2 | ||
|
||
use ( | ||
. | ||
./tools | ||
) |
Oops, something went wrong.