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 5b62727
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 103 deletions.
30 changes: 16 additions & 14 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,8 +43,8 @@ type Authenticator interface {
}
```

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

```go
package main
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
31 changes: 31 additions & 0 deletions cmd/example/demo_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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
package main

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

type DemoAuth struct{}

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

func (s DemoAuth) Resource(username, vhost, resource, name, permission string) rabbitmqauth.Decision {
return rabbitmqauth.Allow
}

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

func (s DemoAuth) VHost(username, vhost, ip string) rabbitmqauth.Decision {
return rabbitmqauth.Allow
}

func (s DemoAuth) Topic(username, vhost, resource, name, permission, routingKey string) rabbitmqauth.Decision {
return rabbitmqauth.Allow
}
31 changes: 0 additions & 31 deletions cmd/example/demo_authenticator.go

This file was deleted.

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
38 changes: 20 additions & 18 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 @@ -22,44 +22,46 @@ func (s Decision) String() string {
return "deny"
}

func validatePostArgs(args []string, r *http.Request) map[string]string {
func postArgsToMap(args []string, r *http.Request) map[string]string {
result := map[string]string{}
for _, s := range args {
result[s] = r.PostFormValue(s)
}
return result
}

func (s *AuthServer) userHandler(w http.ResponseWriter, r *http.Request) {
args := validatePostArgs([]string{"username", "password"}, r)
res, tags := s.authenticator.User(args["username"], args["password"])
func (s *AuthService) userHandler(w http.ResponseWriter, r *http.Request) {
args := postArgsToMap([]string{"username", "password"}, r)
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) {
args := validatePostArgs([]string{"username", "vhost", "ip"}, r)
res := s.authenticator.VHost(args["username"], args["vhost"], args["ip"])
func (s *AuthService) vhostHandler(w http.ResponseWriter, r *http.Request) {
args := postArgsToMap([]string{"username", "vhost", "ip"}, r)
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) {
args := validatePostArgs([]string{"username", "vhost", "resource", "name", "permission", "routing_key"}, r)
res := s.authenticator.Topic(args["username"], args["vhost"],
func (s *AuthService) topicHandler(w http.ResponseWriter, r *http.Request) {
args := postArgsToMap([]string{"username", "vhost", "resource", "name", "permission", "routing_key"}, r)
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) {
args := validatePostArgs([]string{"username", "vhost", "resource", "name", "permission"}, r)
res := s.authenticator.Resource(args["username"], args["vhost"],
func (s *AuthService) resourceHandler(w http.ResponseWriter, r *http.Request) {
args := postArgsToMap([]string{"username", "vhost", "resource", "name", "permission"}, r)
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

0 comments on commit 5b62727

Please sign in to comment.