Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
klaidliadon committed Oct 22, 2024
0 parents commit 4faf457
Show file tree
Hide file tree
Showing 18 changed files with 1,824 additions and 0 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/go.yml
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 ./...
5 changes: 5 additions & 0 deletions README.md
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.
101 changes: 101 additions & 0 deletions common.go
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
}
54 changes: 54 additions & 0 deletions common_test.go
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
}
94 changes: 94 additions & 0 deletions context.go
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
}
26 changes: 26 additions & 0 deletions go.mod
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
)
41 changes: 41 additions & 0 deletions go.sum
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=
6 changes: 6 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
go 1.23.2

use (
.
./tools
)
Loading

0 comments on commit 4faf457

Please sign in to comment.