Skip to content

Commit

Permalink
clean up names
Browse files Browse the repository at this point in the history
  • Loading branch information
jandelgado committed Apr 21, 2021
1 parent 1e181fd commit 4f537bc
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 74 deletions.
28 changes: 15 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ For details see https://github.com/rabbitmq/rabbitmq-server/tree/master/deps/rab

## Build your own service

To build an RabbitMQ HTTP Auth Backend, you just need to implement the provided
`Authenticator` interface, which will be called by `POST` requests to the paths
To build a RabbitMQ HTTP Auth Backend, you just need to implement the provided
`Auth` interface, which will be called by `POST` requests to the paths
`/auth/user`, `/auth/vhost`, `/auth/topic` and `/auth/resource`:

```go
package rabbitmqauth

type Decision bool

type Authenticator interface {
type Auth interface {
// User authenticates the given user. In addition to the decision, the tags
// associated with the user are returned.
User(username, password string) (Decision, string)
Expand All @@ -41,7 +43,7 @@ type Authenticator interface {
}
```

Start a web server using your authenticator and the http router provided
Start a web server using your Auth implementation and the http router provided
by the `rabbitmqauth.AuthServer.NewRouter()` function like:

```go
Expand All @@ -52,24 +54,24 @@ import (
"net/http"
"time"

auth "github.com/jandelgado/rabbitmq-http-auth/pkg"
rabbitmqauth "github.com/jandelgado/rabbitmq-http-auth/pkg"
)

const httpReadTimeout = 10 * time.Second
const httpWriteTimeout = 10 * time.Second

func main() {
authenticator := NewLogInterceptingAuthenticator(DemoAuthenticator{})
s := auth.NewAuthServer(authenticator)
auth := NewLogInterceptingAuth(DemoAuth{})
service := rabbitmqauth.NewAuthService(auth)

srv := &http.Server{
Handler: s.NewRouter(),
server := &http.Server{
Handler: service.NewRouter(),
Addr: fmt.Sprintf(":%d", 8000),
WriteTimeout: httpWriteTimeout,
ReadTimeout: httpReadTimeout,
}

err := srv.ListenAndServe()
err := server.ListenAndServe()

if err != nil {
panic(err)
Expand All @@ -91,7 +93,7 @@ $ curl -XPOST localhost:8000/auth/user -d "username=john&password=test"
deny
```

Since the `DemoAuthenticator` only allows the `guest` user (but with any
Since the `DemoAuth` only allows the `guest` user (but with any
password), this is the expected result.

## Test with RabbitMQ
Expand All @@ -108,12 +110,12 @@ Then in another console, try to publish a message using [rabtap](TODO)
$ echo "hello" | rabtap pub --uri amqp://guest:123@localhost:5672 --exchange amq.topic --routingkey "#"
```

In the docker-compose log, should see the authenticator logging the request:
In the docker-compose log, should see the auth server logging the request:
```
auth-http_1 | 2021/04/18 21:28:01 auth user(u=guest) -> allow [management administrator demo]
```

As the `DemoAuthenticator` allows any password for the guest user, you can
As the `DemoAuth` allows any password for the guest user, you can
try to change the password in the `rabtap` command or try to login on the
[management console](http://localhost:15672) with any password.

Expand Down
16 changes: 8 additions & 8 deletions cmd/example/demo_authenticator.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// rabbitmq-http-auth - demo implementation of the Authenticator interface,
// rabbitmq-http-auth - demo implementation of the Auth interface,
// which allows the user guest with ANY password to be authorized for
// everything.
// (c) copyright 2021 by Jan Delgado
Expand All @@ -8,24 +8,24 @@ import (
auth "github.com/jandelgado/rabbitmq-http-auth/pkg"
)

type DemoAuthenticator struct{}
type DemoAuth struct{}

func (s DemoAuthenticator) String() string {
return "DemoAuthenticator"
func (s DemoAuth) String() string {
return "DemoAuth"
}

func (s DemoAuthenticator) Resource(username, vhost, resource, name, permission string) auth.Decision {
func (s DemoAuth) Resource(username, vhost, resource, name, permission string) auth.Decision {
return true
}

func (s DemoAuthenticator) User(username, password string) (auth.Decision, string) {
func (s DemoAuth) User(username, password string) (auth.Decision, string) {
return username == "guest", "management administrator demo"
}

func (s DemoAuthenticator) VHost(username, vhost, ip string) auth.Decision {
func (s DemoAuth) VHost(username, vhost, ip string) auth.Decision {
return true
}

func (s DemoAuthenticator) Topic(username, vhost, resource, name, permission, routingKey string) auth.Decision {
func (s DemoAuth) Topic(username, vhost, resource, name, permission, routingKey string) auth.Decision {
return true
}
30 changes: 15 additions & 15 deletions cmd/example/log_intercepting_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,42 @@ package main
import (
"log"

auth "github.com/jandelgado/rabbitmq-http-auth/pkg"
rabbitmqauth "github.com/jandelgado/rabbitmq-http-auth/pkg"
)

type LogInterceptingAuthenticator struct {
authenticator auth.Authenticator
type LogInterceptingAuth struct {
auth rabbitmqauth.Auth
}

func NewLogInterceptingAuthenticator(authenticator auth.Authenticator) auth.Authenticator {
return LogInterceptingAuthenticator{authenticator}
func NewLogInterceptingAuth(auth rabbitmqauth.Auth) rabbitmqauth.Auth {
return LogInterceptingAuth{auth}
}

func (s LogInterceptingAuthenticator) String() string {
return "LogInterceptingAuthenticator"
func (s LogInterceptingAuth) String() string {
return "LogInterceptingAuth"
}

func (s LogInterceptingAuthenticator) User(username, password string) (auth.Decision, string) {
res, tags := s.authenticator.User(username, password)
func (s LogInterceptingAuth) User(username, password string) (rabbitmqauth.Decision, string) {
res, tags := s.auth.User(username, password)
log.Printf("auth user(u=%s) -> %v [%s]", username, res, tags)
return res, tags
}

func (s LogInterceptingAuthenticator) VHost(username, vhost, ip string) auth.Decision {
res := s.authenticator.VHost(username, vhost, ip)
func (s LogInterceptingAuth) VHost(username, vhost, ip string) rabbitmqauth.Decision {
res := s.auth.VHost(username, vhost, ip)
log.Printf("auth vhost(u=%s,v=%s,i=%s) -> %v", username, vhost, ip, res)
return res
}

func (s LogInterceptingAuthenticator) Resource(username, vhost, resource, name, permission string) auth.Decision {
res := s.authenticator.Resource(username, vhost, resource, name, permission)
func (s LogInterceptingAuth) Resource(username, vhost, resource, name, permission string) rabbitmqauth.Decision {
res := s.auth.Resource(username, vhost, resource, name, permission)
log.Printf("auth resource(u=%s,v=%s,r=%s,n=%s,p=%s) -> %v",
username, vhost, resource, name, permission, res)
return res
}

func (s LogInterceptingAuthenticator) Topic(username, vhost, resource, name, permission, routingKey string) auth.Decision {
res := s.authenticator.Topic(username, vhost, resource, name, permission, routingKey)
func (s LogInterceptingAuth) Topic(username, vhost, resource, name, permission, routingKey string) rabbitmqauth.Decision {
res := s.auth.Topic(username, vhost, resource, name, permission, routingKey)
log.Printf("auth topic(u=%s,v=%s,r=%s,n=%s,p=%s,k=%s) -> %v",
username, vhost, resource, name, permission, routingKey, res)
return res
Expand Down
16 changes: 8 additions & 8 deletions cmd/example/main.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
// rabbitmq-http-auth - exmple authentication service using a demo authenticator
// (c) copyright 2021 by Jan Delgadoauthenticator}
// rabbitmq-http-auth - exmaple rabbitmq http auth backend service
// (c) copyright 2021 by Jan Delgado
package main

import (
"fmt"
"net/http"
"time"

auth "github.com/jandelgado/rabbitmq-http-auth/pkg"
rabbitmqauth "github.com/jandelgado/rabbitmq-http-auth/pkg"
)

const httpReadTimeout = 10 * time.Second
const httpWriteTimeout = 10 * time.Second

func main() {
authenticator := NewLogInterceptingAuthenticator(DemoAuthenticator{})
s := auth.NewAuthServer(authenticator)
auth := NewLogInterceptingAuth(DemoAuth{})
service := rabbitmqauth.NewAuthService(auth)

srv := &http.Server{
Handler: s.NewRouter(),
server := &http.Server{
Handler: service.NewRouter(),
Addr: fmt.Sprintf(":%d", 8000),
WriteTimeout: httpWriteTimeout,
ReadTimeout: httpReadTimeout,
}

err := srv.ListenAndServe()
err := server.ListenAndServe()

if err != nil {
panic(err)
Expand Down
10 changes: 5 additions & 5 deletions pkg/auth.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// rabbitmq-http-auth - Authenicator interface
// rabbitmq-http-auth - AuthN/AuthZ interface
// (c) copyright 2021 by Jan Delgado
package rabbitmqauth

Expand All @@ -9,13 +9,13 @@ const (
Deny Decision = false
)

// Authenticator instances make the actual decisions on authentication
// requests by RabboitMQ. Every function returns the authentication decision,
// which is always Allow or Deny.
// Auth instances make the actual decisions on authentication and authorization
// requests by RabboitMQ. Every function returns the decision, which is always
// Allow or Deny.
//
// See https://github.com/rabbitmq/rabbitmq-server/tree/master/deps/rabbitmq_auth_backend_http
// for a detailed description.
type Authenticator interface {
type Auth interface {
// User authenticates the given user. In addition to the decision, the tags
// associated with the user are returned.
User(username, password string) (Decision, string)
Expand Down
28 changes: 15 additions & 13 deletions pkg/serve.go → pkg/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (
"net/http"
)

type AuthServer struct {
authenticator Authenticator
type AuthService struct {
auth Auth
}

func NewAuthServer(authenticator Authenticator) AuthServer {
return AuthServer{authenticator}
func NewAuthService(auth Auth) AuthService {
return AuthService{auth}
}

func (s Decision) String() string {
Expand All @@ -30,36 +30,38 @@ func validatePostArgs(args []string, r *http.Request) map[string]string {
return result
}

func (s *AuthServer) userHandler(w http.ResponseWriter, r *http.Request) {
func (s *AuthService) userHandler(w http.ResponseWriter, r *http.Request) {
args := validatePostArgs([]string{"username", "password"}, r)
res, tags := s.authenticator.User(args["username"], args["password"])
res, tags := s.auth.User(args["username"], args["password"])
if res {
fmt.Fprintf(w, "%s [%s]", res, tags)
} else {
fmt.Fprintf(w, "%s", res)
}
}

func (s *AuthServer) vhostHandler(w http.ResponseWriter, r *http.Request) {
func (s *AuthService) vhostHandler(w http.ResponseWriter, r *http.Request) {
args := validatePostArgs([]string{"username", "vhost", "ip"}, r)
res := s.authenticator.VHost(args["username"], args["vhost"], args["ip"])
res := s.auth.VHost(args["username"], args["vhost"], args["ip"])
fmt.Fprintf(w, "%s", res)
}

func (s *AuthServer) topicHandler(w http.ResponseWriter, r *http.Request) {
func (s *AuthService) topicHandler(w http.ResponseWriter, r *http.Request) {
args := validatePostArgs([]string{"username", "vhost", "resource", "name", "permission", "routing_key"}, r)
res := s.authenticator.Topic(args["username"], args["vhost"],
res := s.auth.Topic(args["username"], args["vhost"],
args["resource"], args["name"], args["permission"], args["routing_key"])
fmt.Fprintf(w, "%s", res)
}

func (s *AuthServer) resourceHandler(w http.ResponseWriter, r *http.Request) {
func (s *AuthService) resourceHandler(w http.ResponseWriter, r *http.Request) {
args := validatePostArgs([]string{"username", "vhost", "resource", "name", "permission"}, r)
res := s.authenticator.Resource(args["username"], args["vhost"],
res := s.auth.Resource(args["username"], args["vhost"],
args["resource"], args["name"], args["permission"])
fmt.Fprintf(w, "%s", res)
}

// postHandler passes a request to the given handler only if it is a POST
// request, otherwise returning a 405
func postHandler(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
Expand All @@ -71,7 +73,7 @@ func postHandler(handler func(http.ResponseWriter, *http.Request)) func(http.Res
}
}

func (s *AuthServer) NewRouter() http.Handler {
func (s *AuthService) NewRouter() http.Handler {
router := http.NewServeMux()
router.HandleFunc("/auth/user", postHandler(s.userHandler))
router.HandleFunc("/auth/vhost", postHandler(s.vhostHandler))
Expand Down
24 changes: 12 additions & 12 deletions pkg/serve_test.go → pkg/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,25 @@ import (
"github.com/stretchr/testify/require"
)

type MockAuthenticator struct{}
type MockAuth struct{}

func (s MockAuthenticator) String() string {
return "MockAuthenticator"
func (s MockAuth) String() string {
return "MockAuth"
}

func (s MockAuthenticator) User(username, password string) (Decision, string) {
func (s MockAuth) User(username, password string) (Decision, string) {
return username == "a" && password == "b", "management"
}

func (s MockAuthenticator) VHost(username, vhost, ip string) Decision {
func (s MockAuth) VHost(username, vhost, ip string) Decision {
return username == "a" && vhost == "b" && ip == "c"
}

func (s MockAuthenticator) Resource(username, vhost, resource, name, permission string) Decision {
func (s MockAuth) Resource(username, vhost, resource, name, permission string) Decision {
return username == "a" && vhost == "b" && resource == "c" && name == "d" && permission == "e"
}

func (s MockAuthenticator) Topic(username, vhost, resource, name, permission, routingKey string) Decision {
func (s MockAuth) Topic(username, vhost, resource, name, permission, routingKey string) Decision {
return username == "a" && vhost == "b" && resource == "c" && name == "d" && permission == "e" && routingKey == "f"
}

Expand All @@ -50,7 +50,7 @@ func TestPostHandlerReturns405OnGET(t *testing.T) {
cases := []string{"/auth/user", "/auth/vhost", "/auth/resource", "/auth/topic"}

for _, path := range cases {
auth := NewAuthServer(MockAuthenticator{})
auth := NewAuthService(MockAuth{})
handlerFunc := auth.NewRouter()

req, err := http.NewRequest("GET", path, nil)
Expand All @@ -65,7 +65,7 @@ func TestPostHandlerReturns405OnGET(t *testing.T) {
}

func TestUserRequestIsAllowedOnlyOnValidRequests(t *testing.T) {
auth := NewAuthServer(MockAuthenticator{})
auth := NewAuthService(MockAuth{})
srv := httptest.NewServer(auth.NewRouter())
defer srv.Close()

Expand All @@ -86,7 +86,7 @@ func TestUserRequestIsAllowedOnlyOnValidRequests(t *testing.T) {
}

func TestVHostRequestIsAllowedOnlyOnValidRequests(t *testing.T) {
auth := NewAuthServer(MockAuthenticator{})
auth := NewAuthService(MockAuth{})
srv := httptest.NewServer(auth.NewRouter())
defer srv.Close()

Expand All @@ -107,7 +107,7 @@ func TestVHostRequestIsAllowedOnlyOnValidRequests(t *testing.T) {
}

func TestResourceRequestIsAllowedOnlyOnValidRequests(t *testing.T) {
auth := NewAuthServer(MockAuthenticator{})
auth := NewAuthService(MockAuth{})
srv := httptest.NewServer(auth.NewRouter())
defer srv.Close()

Expand All @@ -128,7 +128,7 @@ func TestResourceRequestIsAllowedOnlyOnValidRequests(t *testing.T) {
}

func TestTopicRequestIsAllowedOnlyOnValidRequests(t *testing.T) {
auth := NewAuthServer(MockAuthenticator{})
auth := NewAuthService(MockAuth{})
srv := httptest.NewServer(auth.NewRouter())
defer srv.Close()

Expand Down

0 comments on commit 4f537bc

Please sign in to comment.