added docker compose and ci/cd

This commit is contained in:
Valentin Kolb 2023-11-20 16:32:32 +01:00
parent b6157c67ae
commit bcb143ca76
7 changed files with 156 additions and 22 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
.env.local
.gitignore
.gitlab-ci.yml
docker-compose.yml
Readme.md

15
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,15 @@
stages:
- build
build-container:
stage: build
image:
name: gcr.io/kaniko-project/executor:v1.9.0-debug
entrypoint: [ "" ]
script:
- /kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
only:
- main

40
Dockerfile Normal file
View File

@ -0,0 +1,40 @@
# Start from the official Go image to create a build artifact.
# This is the first stage of a multi-stage build.
FROM golang:1.19 as builder
# Set the working directory inside the container.
WORKDIR /build
# Copy go.mod and go.sum to download dependencies.
COPY go.mod .
# Download Go modules (dependencies).
RUN go mod download
# Copy the rest of the source code.
COPY . .
# https://stackoverflow.com/questions/36279253/go-compiled-binary-wont-run-in-an-alpine-docker-container-on-ubuntu-host
ENV CGO_ENABLED=0
# Build the Go app as a static binary.
# You might need to add tags or other build arguments depending on your application.
RUN go build -o pocketbase main/main.go
# Start from a smaller image to make the final image smaller.
FROM alpine:latest
# Set the working directory inside the container.
WORKDIR /app
# Copy the statically-linked binary from the builder stage.
COPY --from=builder /build/pocketbase .
# Expose port 8090 to the outside world.
EXPOSE 8090
# Update Path
ENV PATH="/app:${PATH}"
# Command to run the executable.
CMD ["./pocketbase", "serve", "--http=0.0.0.0:8090", "--dir=/pb_data"]

View File

@ -42,13 +42,28 @@ Pocketbase selbst bietet folgende Features:
- Pocketbase bietet ein JavaScript SDK an, das alle Features von
Pocketbase unterstützt.
## Admin UI
Pocketbase bietet eine Admin UI an, die unter `https://<pocketbase-url>/_/` erreichbar ist.
Der Login hierzu ist im SysPass unter *Pocketbase* zu finden.
In der Admin UI können Tabellen erstellt, bearbeitet und gelöscht werden.
Außerdem können die API Rules bearbeitet werden, die festlegen, welche
Funktionen für die einzelnen Tabellen und User erlaubt sind.
## Entwickeln
todo
```bash
git clone ...
cd backend
go run main.go serve # --debug=0 for no debug output
```
## Installation
todo
Das Projekt kann mittels Docker installiert werden.
Durch die Gitlab CI/CD Pipeline wird bei jedem Push auf `main` ein neues Docker
Image erstellt. Dieses muss dann nur noch auf dem Server deployt werden.
## Projekt Struktur
@ -152,6 +167,12 @@ für diese Collection deaktiviert werden (Username, E-Mail, OAuth2).
Mehr dazu unter dem LDAP Login Package.
#### DB Util
In der `db.go` Datei sind alle Datenbank Funktionen implementiert.
Hier gibt es Methoden um User oder Groups zu finden, die dabei automatisch
die Beziehungen zu anderen Tabellen laden (`memberOf`).
### LDAP Login
Dieses Package ist für die Authentifizierung zuständig.
@ -168,7 +189,7 @@ Das Package benötigt folgende ENV Variablen:
Dieses Package für der Api die folgende Route hinzu:
`GET /api/ldap/login`
`POST /api/ldap/login`
Diese Route nimmt die Parameter `username` und `password` als json Body entgegen:
@ -179,11 +200,9 @@ Diese Route nimmt die Parameter `username` und `password` als json Body entgegen
}
```
Dabei wird das Passwort mit dem LDAP Server abgeglichen und falls
erfolgreich ein Token und das ldap_user Modell zurückgegeben.
Das Passwort wird mit dem LDAP Server abgeglichen und nach einer
erfolgreichen Authentifizierung ein Token und das ldap_user Modell
zurückgegeben. Die `memberOf` Beziehungen werden dabei automatisch geladen.
Das Passwort wird nicht in der Datenbank gespeichert.
Diese Response ist identisch mit der standard Pocketbase Authentifizierung.
#### Token Refresh
todo
Diese Response ist identisch mit der standard Pocketbase Authentifizierung (z.B. AuthWithPassword).

33
docker-compose.yml Normal file
View File

@ -0,0 +1,33 @@
services:
pocketbase:
image: gitlab.uni-ulm.de:5050/stuve-it/it-tools/backend
#command:
# - "--debug"
container_name: stuve_it_backend
restart: unless-stopped
volumes:
- pb_data:/pb_data
ports:
- 8090:8090
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:8090/api/health || exit 1
interval: 5s
timeout: 5s
retries: 5
environment:
LDAP_URL: "ldap://dc.stuve.uni-ulm.de"
LDAP_BIND_DN: "cn=ldapsync,ou=systemaccounts,ou=user,dc=stuve,dc=uni-ulm,dc=de"
LDAP_BIND_PASSWORD: "************"
LDAP_BASE_DN: "ou=useraccounts,ou=user,dc=stuve,dc=uni-ulm,dc=de"
LDAP_USER_FILTER: "(|(objectCategory=person)(objectClass=user))"
LDAP_GROUP_FILTER: "(objectClass=group)"
LDAP_GROUP_BASE_DN: "ou=groups,ou=user,dc=stuve,dc=uni-ulm,dc=de"
LDAP_SYNC_SCHEDULE: "*/1 * * * *"
volumes:
pb_data:

View File

@ -8,6 +8,7 @@ import (
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/ldapSync"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
"os"
)
@ -27,11 +28,16 @@ import (
// if the user is authenticated successfully the endpoint returns and apis.RecordAuthResponse
func InitLDAPLogin(app *pocketbase.PocketBase, e *core.ServeEvent) error {
e.Router.GET("/api/ldap/login", func(c echo.Context) error {
// add endpoint to app
logger.LogInfoF("Adding LDAP Login Endpoint")
e.Router.POST("/api/ldap/login", func(c echo.Context) error {
logger.LogInfoF("LDAP Login")
// step 1: get data from request
data := struct {
CN string `json:"cn" form:"cn"`
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
}{}
if err := c.Bind(&data); err != nil {
@ -39,7 +45,7 @@ func InitLDAPLogin(app *pocketbase.PocketBase, e *core.ServeEvent) error {
}
// step 2: get ldap user by cn from ldapUsers table
record, err := ldapSync.GetLdapUserByCN(app, data.CN)
record, err := ldapSync.GetLdapUserByCN(app, data.Username)
// if user does not exist in ldapUsers table return error
if err != nil {
@ -57,7 +63,7 @@ func InitLDAPLogin(app *pocketbase.PocketBase, e *core.ServeEvent) error {
defer conn.Close()
// step 4: bind to ldap server with user credentials from request
err = conn.Bind(data.CN, data.Password)
err = conn.Bind(data.Username, data.Password)
if err != nil {
// if bind fails return error - invalid credentials
return apis.NewBadRequestError("Invalid credentials", err)

View File

@ -89,6 +89,12 @@ func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
record = res
} else { // if record does not exist, create it
record = models.NewRecord(collection)
// refresh token key only if new record is created
// if record is updated, the token key should not be changed because it is used to authenticate the user
// if the token key is changed, the user will be logged out
if err := record.RefreshTokenKey(); err != nil {
return err
}
}
accountExpires, _ := ldapTimeToUnixTime(ldapUser.accountExpires)
@ -117,8 +123,6 @@ func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
record.Set("cn", ldapUser.cn)
record.Set("memberOf", groups)
record.RefreshTokenKey()
if err := app.Dao().SaveRecord(record); err != nil {
return fmt.Errorf("failed to upsert user with dn: %s - %w", ldapUser.dn, err)
}
@ -126,18 +130,30 @@ func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
return nil
}
// GetLdapGroupByDN This function returns a record from the ldap groups table by dn. It also expands the memberOf field.
func GetLdapGroupByDN(app *pocketbase.PocketBase, dn string) (*models.Record, error) {
return app.Dao().FindFirstRecordByFilter(
record, err := app.Dao().FindFirstRecordByFilter(
ldapGroupsTableName,
"dn = {:dn}",
dbx.Params{"dn": dn},
)
if err != nil {
return nil, err
}
if errs := app.Dao().ExpandRecord(record, []string{"memberOf"}, nil); len(errs) > 0 {
return nil, fmt.Errorf("ldap group - failed to expand: %v", errs)
}
return record, nil
}
// GetLdapUserByCN This function returns a record from the ldap users table by cn. It also expands the memberOf field.
func GetLdapUserByCN(app *pocketbase.PocketBase, cn string) (*models.Record, error) {
return app.Dao().FindFirstRecordByFilter(
ldapUsersTableName,
"cn = {:cn}",
dbx.Params{"cn": cn},
)
record, err := app.Dao().FindAuthRecordByUsername(ldapUsersTableName, cn)
if err != nil {
return nil, err
}
if errs := app.Dao().ExpandRecord(record, []string{"memberOf"}, nil); len(errs) > 0 {
return nil, fmt.Errorf("ldap user - failed to expand: %v", errs)
}
return record, nil
}