Compare commits
2 Commits
61590f684e
...
9488f82b18
Author | SHA1 | Date | |
---|---|---|---|
9488f82b18 | |||
b880f2f48b |
|
@ -12,7 +12,6 @@ steps:
|
||||||
- docker-compose down -v --remove-orphans -t0
|
- docker-compose down -v --remove-orphans -t0
|
||||||
- docker-compose build
|
- docker-compose build
|
||||||
- docker-compose --profile tests build
|
- docker-compose --profile tests build
|
||||||
- docker-compose run --rm db-migrations
|
|
||||||
- docker-compose up -d
|
- docker-compose up -d
|
||||||
- docker-compose run --rm tests
|
- docker-compose run --rm tests
|
||||||
|
|
||||||
|
|
|
@ -33,3 +33,12 @@ Obtain an admin GWT: `curl -d '"api-key-goes-here"' -H "Content-Type: applicatio
|
||||||
Use a bearer token to make a call: `curl -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/accounts/{accountID}`
|
Use a bearer token to make a call: `curl -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/accounts/{accountID}`
|
||||||
|
|
||||||
Create account: `curl -d '{"name": "Bosse", "password": "Hemligt", "fields": [{ "name":"role", "values":["user"]}]}' -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/account`
|
Create account: `curl -d '{"name": "Bosse", "password": "Hemligt", "fields": [{ "name":"role", "values":["user"]}]}' -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/account`
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
For local development, run with .env like: `eval $(cat .env) go run src/main.go`
|
||||||
|
|
||||||
|
To regenerate the swagger docs folder:
|
||||||
|
|
||||||
|
1. Make sure you have swag installed: https://github.com/swaggo/swag
|
||||||
|
2. cd src && swag init
|
1
go.mod
1
go.mod
|
@ -9,7 +9,6 @@ require (
|
||||||
github.com/gofiber/fiber/v2 v2.44.0
|
github.com/gofiber/fiber/v2 v2.44.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/jackc/pgx/v4 v4.18.1
|
github.com/jackc/pgx/v4 v4.18.1
|
||||||
github.com/joho/godotenv v1.5.1
|
|
||||||
github.com/swaggo/swag v1.16.1
|
github.com/swaggo/swag v1.16.1
|
||||||
golang.org/x/crypto v0.8.0
|
golang.org/x/crypto v0.8.0
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -101,8 +101,6 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
|
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
|
||||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
|
|
@ -3,7 +3,7 @@ package db
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"gitea.larvit.se/pwrpln/auth-api/pkgs/utils"
|
"gitea.larvit.se/pwrpln/auth-api/src/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RenewalTokenCreate obtain a new renewal token
|
// RenewalTokenCreate obtain a new renewal token
|
|
@ -37,9 +37,10 @@ func (h Handlers) AccountDel(c *fiber.Ctx) error {
|
||||||
|
|
||||||
err := h.Db.AccountDel(accountID)
|
err := h.Db.AccountDel(accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "No account found for given accountID" {
|
if err.Error() == "no account found for given accountID" {
|
||||||
return c.Status(404).JSON([]ResJSONError{{Error: err.Error()}})
|
return c.Status(404).JSON([]ResJSONError{{Error: err.Error()}})
|
||||||
} else {
|
} else {
|
||||||
|
h.Log.Error("Database error when trying to remove account", "err", err.Error())
|
||||||
return c.Status(500).JSON([]ResJSONError{{Error: "Database error when trying to remove account"}})
|
return c.Status(500).JSON([]ResJSONError{{Error: "Database error when trying to remove account"}})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.larvit.se/pwrpln/auth-api/pkgs/db"
|
"gitea.larvit.se/pwrpln/auth-api/src/db"
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
|
@ -3,8 +3,8 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.larvit.se/pwrpln/auth-api/pkgs/db"
|
"gitea.larvit.se/pwrpln/auth-api/src/db"
|
||||||
"gitea.larvit.se/pwrpln/auth-api/pkgs/utils"
|
"gitea.larvit.se/pwrpln/auth-api/src/utils"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
|
@ -1,7 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.larvit.se/pwrpln/auth-api/pkgs/db"
|
"gitea.larvit.se/pwrpln/auth-api/src/db"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
|
@ -1,7 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.larvit.se/pwrpln/auth-api/pkgs/db"
|
"gitea.larvit.se/pwrpln/auth-api/src/db"
|
||||||
"gitea.larvit.se/pwrpln/go_log"
|
"gitea.larvit.se/pwrpln/go_log"
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
)
|
)
|
|
@ -6,21 +6,20 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.larvit.se/pwrpln/auth-api/pkgs/db"
|
"gitea.larvit.se/pwrpln/auth-api/src/db"
|
||||||
h "gitea.larvit.se/pwrpln/auth-api/pkgs/handlers"
|
h "gitea.larvit.se/pwrpln/auth-api/src/handlers"
|
||||||
"gitea.larvit.se/pwrpln/go_log"
|
"gitea.larvit.se/pwrpln/go_log"
|
||||||
swagger "github.com/arsmn/fiber-swagger/v2"
|
swagger "github.com/arsmn/fiber-swagger/v2"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
"github.com/joho/godotenv"
|
|
||||||
|
|
||||||
// docs are generated by Swag CLI, you have to import them.
|
// docs are generated by Swag CLI, you have to import them.
|
||||||
_ "gitea.larvit.se/pwrpln/auth-api/docs"
|
_ "gitea.larvit.se/pwrpln/auth-api/src/docs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Don't put in utils, because it creates import cycle with db... just left it here for now
|
// Don't put in utils, because it creates import cycle with db... just left it here for now
|
||||||
func createAdminAccount(Db db.Db, log go_log.Log) {
|
func createAdminAccount(Db db.Db, log go_log.Log, ADMIN_API_KEY string) {
|
||||||
adminAccountID, uuidErr := uuid.NewRandom()
|
adminAccountID, uuidErr := uuid.NewRandom()
|
||||||
if uuidErr != nil {
|
if uuidErr != nil {
|
||||||
log.Error("Could not create new Uuid", "err", uuidErr.Error())
|
log.Error("Could not create new Uuid", "err", uuidErr.Error())
|
||||||
|
@ -29,7 +28,7 @@ func createAdminAccount(Db db.Db, log go_log.Log) {
|
||||||
_, adminAccountErr := Db.AccountCreate(db.AccountCreateInput{
|
_, adminAccountErr := Db.AccountCreate(db.AccountCreateInput{
|
||||||
ID: adminAccountID,
|
ID: adminAccountID,
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
APIKey: os.Getenv("ADMIN_API_KEY"),
|
APIKey: ADMIN_API_KEY,
|
||||||
Password: "",
|
Password: "",
|
||||||
Fields: []db.AccountCreateInputFields{{Name: "role", Values: []string{"admin"}}},
|
Fields: []db.AccountCreateInputFields{{Name: "role", Values: []string{"admin"}}},
|
||||||
})
|
})
|
||||||
|
@ -55,11 +54,6 @@ func createAdminAccount(Db db.Db, log go_log.Log) {
|
||||||
func main() {
|
func main() {
|
||||||
log := go_log.GetLog()
|
log := go_log.GetLog()
|
||||||
|
|
||||||
err := godotenv.Load()
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Error loading .env file, this could be ok if the env file does not exist", "err", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.Getenv("JWT_SHARED_SECRET") == "changeMe" {
|
if os.Getenv("JWT_SHARED_SECRET") == "changeMe" {
|
||||||
log.Warn("JWT_SHARED_SECRET ENV is not set, using very insecure \"changeMe\"")
|
log.Warn("JWT_SHARED_SECRET ENV is not set, using very insecure \"changeMe\"")
|
||||||
}
|
}
|
||||||
|
@ -79,13 +73,18 @@ func main() {
|
||||||
log.MinLogLvl = minLogLvl
|
log.MinLogLvl = minLogLvl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ADMIN_API_KEY := os.Getenv("ADMIN_API_KEY")
|
||||||
|
WEB_BIND_HOST := os.Getenv("WEB_BIND_HOST")
|
||||||
|
DATABASE_URL := os.Getenv("DATABASE_URL")
|
||||||
|
|
||||||
jwtKey := []byte(os.Getenv("JWT_SHARED_SECRET"))
|
jwtKey := []byte(os.Getenv("JWT_SHARED_SECRET"))
|
||||||
|
|
||||||
dbPool, err := pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
dbPool, err := pgxpool.Connect(context.Background(), DATABASE_URL)
|
||||||
for err != nil {
|
for err != nil {
|
||||||
log.Warn("Failed to open connection to PostgreSQL database, retrying in 1 second", "err", err.Error())
|
log.Warn("Failed to open connection to PostgreSQL database, retrying in 1 second", "err", err.Error())
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
dbPool, err = pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
dbPool, err = pgxpool.Connect(context.Background(), DATABASE_URL)
|
||||||
}
|
}
|
||||||
log.Verbose("Connected to PostgreSQL database")
|
log.Verbose("Connected to PostgreSQL database")
|
||||||
defer dbPool.Close()
|
defer dbPool.Close()
|
||||||
|
@ -95,7 +94,7 @@ func main() {
|
||||||
Db := db.Db{DbPool: dbPool, Log: log}
|
Db := db.Db{DbPool: dbPool, Log: log}
|
||||||
handlers := h.Handlers{Db: Db, JwtKey: jwtKey, Log: log}
|
handlers := h.Handlers{Db: Db, JwtKey: jwtKey, Log: log}
|
||||||
|
|
||||||
createAdminAccount(Db, log)
|
createAdminAccount(Db, log, ADMIN_API_KEY)
|
||||||
|
|
||||||
// Log all requests
|
// Log all requests
|
||||||
app.Use(handlers.LogReq)
|
app.Use(handlers.LogReq)
|
||||||
|
@ -116,14 +115,13 @@ func main() {
|
||||||
app.Post("/renew-token", handlers.RenewToken)
|
app.Post("/renew-token", handlers.RenewToken)
|
||||||
app.Put("/accounts/:accountID/fields", handlers.AccountUpdateFields)
|
app.Put("/accounts/:accountID/fields", handlers.AccountUpdateFields)
|
||||||
|
|
||||||
log.Info("Starting web server", "WEB_BIND_HOST", os.Getenv("WEB_BIND_HOST"))
|
log.Info("Starting web server", "WEB_BIND_HOST", WEB_BIND_HOST)
|
||||||
|
|
||||||
webBindHost := os.Getenv("WEB_BIND_HOST")
|
err = app.Listen(WEB_BIND_HOST)
|
||||||
err = app.Listen(webBindHost)
|
|
||||||
for err != nil {
|
for err != nil {
|
||||||
log.Warn("Could not start web server", "err", err.Error(), "WEB_BIND_HOST", webBindHost)
|
log.Warn("Could not start web server", "err", err.Error(), "WEB_BIND_HOST", WEB_BIND_HOST)
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
err = app.Listen(webBindHost)
|
err = app.Listen(WEB_BIND_HOST)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Web server closed, shutting down")
|
log.Info("Web server closed, shutting down")
|
|
@ -1,3 +1,3 @@
|
||||||
ADMIN_API_KEY=hihi
|
ADMIN_API_KEY=hihi
|
||||||
AUTH_URL=http://localhost:4000
|
AUTH_URL=http://127.0.0.1:4000
|
||||||
JWT_SHARED_SECRET=hihi
|
JWT_SHARED_SECRET=hihi
|
||||||
|
|
|
@ -6,7 +6,7 @@ import setConfig from '../test-helpers/config.js';
|
||||||
test('test-cases/00start.js: Wait for auth API to be ready', async t => {
|
test('test-cases/00start.js: Wait for auth API to be ready', async t => {
|
||||||
setConfig({ printConfig: true });
|
setConfig({ printConfig: true });
|
||||||
|
|
||||||
const backendHealthCheck = await got(process.env.AUTH_URL, { retry: 2000 });
|
const backendHealthCheck = await got(process.env.AUTH_URL, { retry: { limit: 2000 }});
|
||||||
|
|
||||||
t.equal(backendHealthCheck.statusCode, 200, 'Auth API should answer with status code 200');
|
t.equal(backendHealthCheck.statusCode, 200, 'Auth API should answer with status code 200');
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,7 +54,7 @@ test('test-cases/01basic.js: GETting the admin account, with the token we just o
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test-cases/01basic.js: Creating a new account', async t => {
|
test('test-cases/01basic.js: Creating a new account', async t => {
|
||||||
const res = await got.post(`${process.env.AUTH_URL}/account`, {
|
const res = await got.post(`${process.env.AUTH_URL}/accounts`, {
|
||||||
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
||||||
json: {
|
json: {
|
||||||
fields: [
|
fields: [
|
||||||
|
@ -79,7 +79,7 @@ test('test-cases/01basic.js: Creating a new account', async t => {
|
||||||
t.notEqual(user.apiKey, undefined, 'The new account should have an apiKey');
|
t.notEqual(user.apiKey, undefined, 'The new account should have an apiKey');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await got.post(`${process.env.AUTH_URL}/account`, {
|
await got.post(`${process.env.AUTH_URL}/accounts`, {
|
||||||
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
||||||
json: {
|
json: {
|
||||||
fields: [{name: 'role',values: ['user'],}],
|
fields: [{name: 'role',values: ['user'],}],
|
||||||
|
@ -187,7 +187,7 @@ test('test-cases/01basic.js: Remove an account', async t => {
|
||||||
await got.delete(`${process.env.AUTH_URL}/accounts/a423e690-74b9-4f37-9976-f5bf75a5ea32`, {
|
await got.delete(`${process.env.AUTH_URL}/accounts/a423e690-74b9-4f37-9976-f5bf75a5ea32`, {
|
||||||
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
retry: 0,
|
retry: { limit: 0 },
|
||||||
});
|
});
|
||||||
t.fail('Response status for DELETing an account that does not exist should be 404');
|
t.fail('Response status for DELETing an account that does not exist should be 404');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -197,7 +197,7 @@ test('test-cases/01basic.js: Remove an account', async t => {
|
||||||
const delRes = await got.delete(`${process.env.AUTH_URL}/accounts/${user.id}`, {
|
const delRes = await got.delete(`${process.env.AUTH_URL}/accounts/${user.id}`, {
|
||||||
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
retry: 0,
|
retry: { limit: 0 },
|
||||||
});
|
});
|
||||||
|
|
||||||
t.equal(delRes.statusCode, 204, 'Response status for DELETE should be 204');
|
t.equal(delRes.statusCode, 204, 'Response status for DELETE should be 204');
|
||||||
|
@ -206,7 +206,7 @@ test('test-cases/01basic.js: Remove an account', async t => {
|
||||||
await got(`${process.env.AUTH_URL}/accounts/${user.id}`, {
|
await got(`${process.env.AUTH_URL}/accounts/${user.id}`, {
|
||||||
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
retry: 0,
|
retry: { limit: 0 },
|
||||||
});
|
});
|
||||||
t.fail('Response status for GETing the account again should be 404');
|
t.fail('Response status for GETing the account again should be 404');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user