Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return error not *ErrUmbrella + add a sample app #3

Merged
merged 3 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/cmd/example1/example1
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.DEFAULT_GOAL := help

.PHONY: help test

test: ## Runs tests
go test

run-example1: clean ## Runs sample app
@echo "* Creating docker container with PostgreSQL"
docker run --name umbrella-example1 -d -e POSTGRES_PASSWORD=upass -e POSTGRES_USER=uuser -e POSTGRES_DB=udb -p 54321:5432 postgres:13
@echo "* Sleeping for 10 seconds to give database time to initialize..."
@sleep 10
@echo "* Building and starting application..."
cd cmd/example1 && go build .
cd cmd/example1 && ./example1
@echo "* Removing previously created docker container..."


clean: ## Removes all created dockers
docker rm -f umbrella-example1

help: ## Displays this help
@awk 'BEGIN {FS = ":.*##"; printf "$(MAKEFILE_NAME)\n\nUsage:\n make \033[1;36m<target>\033[0m\n\nTargets:\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[1;36m%-25s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
59 changes: 20 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,26 @@ Package umbrella provides a simple authentication mechanism for an HTTP endpoint
5. [Motivation](#motivation)

## Sample code
The following code snippet shows how the module can be used.

```go
// database connection
dbConn, _ = sql.Open("postgres", "host=localhost user=myuser password=mypass port=5432 dbname=mydb sslmode=disable")

// umbrella controller
u := NewUmbrella(dbConn, "tblprefix_", &JWTConfig{
Key: "SomeSecretKey--.",
Issuer: "SomeIssuer",
ExpirationMinutes: 15,
}, nil)

// create db tables
_ := u.CreateDBTables()

// http server
// uri with registration, activation, login (returns auth token), logout endpoint
http.Handle("/umbrella/", u.GetHTTPHandler("/umbrella/"))
// restricted stuff that requires signing in (a token in http header)
http.Handle("/restricted_stuff/", u.GetHTTPHandlerWrapper(
getRestrictedStuffHTTPHandler(),
umbrella.HandlerConfig{},
))
http.ListenAndServe(":8001", nil)

// wrap http handler with a check for logged user
func getRestrictedStuffHTTPHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := umbrella.GetUserIDFromRequest(r)
if userID != 0 {
w.WriteHeader(http.StatusOK)
w.Write([]byte("RestrictedAreaContent"))
} else {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("NoAccess"))
}
})
}
A working application can be found in the `cmd/example1`. Type `make run-example1` to start an HTTP server and check the endpoints as shown below. jq is used to parse out the token from JSON output, however, it can be done manually as well.

```bash
# run the application
% make run-example1
# ...some output...

# sign in to get a token
% UMB_TOKEN=$(curl -s -X POST -d "[email protected]&password=admin" http://localhost:8001/umbrella/login | jq -r '.data.token')

# call restricted endpoint without the token
% curl http://localhost:8001/secret_stuff/
YouHaveToBeLoggedIn

# call it again with token
% curl -H "Authorization: Bearer $UMB_TOKEN" http://localhost:8001/secret_stuff/
SecretStuffOnlyForAdmin%

# remove temporary postgresql docker
make clean
```

## Database connection
Expand Down
72 changes: 72 additions & 0 deletions cmd/example1/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"database/sql"
"log"
"net/http"

"github.com/go-phings/umbrella"
_ "github.com/lib/pq"
)

const dbDSN = "host=localhost user=uuser password=upass port=54321 dbname=udb sslmode=disable"
const tblPrefix = "p_"

func main() {
db, err := sql.Open("postgres", dbDSN)
if err != nil {
log.Fatal("Error connecting to db")
}

// create umbrella controller
u := *umbrella.NewUmbrella(db, tblPrefix, &umbrella.JWTConfig{
Key: "someSecretKey",
Issuer: "someIssuer",
ExpirationMinutes: 15,
}, &umbrella.UmbrellaConfig{
TagName: "ui",
})

// create database tables
err = u.CreateDBTables()
if err != nil {
log.Fatalf("error creating database tables: %s", err.Error())
}

// create admin user
key, err := u.CreateUser("[email protected]", "admin", map[string]string{
"Name": "admin",
})
if err != nil {
log.Fatalf("error with creating admin: %s", err.Error())
}
err = u.ConfirmEmail(key)
if err != nil {
log.Fatalf("error with confirming admin email: %s", err.Error())
}

// /umbrella/{login,logout,register,confirm}
http.Handle("/umbrella/", u.GetHTTPHandler("/umbrella/"))

// secret stuff
http.Handle("/secret_stuff/", u.GetHTTPHandlerWrapper(secretStuff(), umbrella.HandlerConfig{}))

log.Fatal(http.ListenAndServe(":8001", nil))
}

func secretStuff() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := umbrella.GetUserIDFromRequest(r)
switch userID {
case 1:
w.WriteHeader(http.StatusOK)
w.Write([]byte("SecretStuffOnlyForAdmin"))
case 0:
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("YouHaveToBeLoggedIn"))
default:
w.WriteHeader(http.StatusOK)
w.Write([]byte("SecretStuffForOtherUser"))
}
})
}
4 changes: 2 additions & 2 deletions err.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ type ErrUmbrella struct {
Err error
}

func (e *ErrUmbrella) Error() string {
func (e ErrUmbrella) Error() string {
return e.Err.Error()
}

func (e *ErrUmbrella) Unwrap() error {
func (e ErrUmbrella) Unwrap() error {
return e.Err
}
Loading