feat: qr-code api, major refactor, objectGUID as user id and group id
Build and Push Docker image / build-and-push (push) Failing after 29s
Details
Build and Push Docker image / build-and-push (push) Failing after 29s
Details
This commit is contained in:
parent
382388eb91
commit
d51a92f6e1
|
@ -1,5 +1,6 @@
|
||||||
.env.local
|
.env.local
|
||||||
.gitignore
|
.gitignore
|
||||||
.gitlab-ci.yml
|
.gitea
|
||||||
|
.pb_data
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
Readme.md
|
Readme.md
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: Build and Push Docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Log in to StuVe Gitea Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: git.stuve.uni-ulm.de
|
||||||
|
username: ${{ secrets.USER_NAME }}
|
||||||
|
password: ${{ secrets.USER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend:latest
|
||||||
|
|
||||||
|
#- name: Trigger webhook
|
||||||
|
# run: curl -X POST ${{ secrets.WEBHOOK_URL }}
|
|
@ -10,6 +10,7 @@ pb_data
|
||||||
|
|
||||||
# env
|
# env
|
||||||
*.env.local
|
*.env.local
|
||||||
|
*.env
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
|
@ -1,15 +0,0 @@
|
||||||
stages:
|
|
||||||
- build
|
|
||||||
|
|
||||||
build-container:
|
|
||||||
stage: build
|
|
||||||
image:
|
|
||||||
name: gcr.io/kaniko-project/executor:debug
|
|
||||||
entrypoint: [ "" ]
|
|
||||||
script:
|
|
||||||
- /kaniko/executor
|
|
||||||
--context "${CI_PROJECT_DIR}"
|
|
||||||
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
|
|
||||||
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
|
|
||||||
only:
|
|
||||||
- main
|
|
|
@ -2,6 +2,10 @@
|
||||||
# This is the first stage of a multi-stage build.
|
# This is the first stage of a multi-stage build.
|
||||||
FROM golang:1.19 as builder
|
FROM golang:1.19 as builder
|
||||||
|
|
||||||
|
# Set labels
|
||||||
|
LABEL maintainer="Valentin Kolb"
|
||||||
|
LABEL version="1.0"
|
||||||
|
|
||||||
# Set the working directory inside the container.
|
# Set the working directory inside the container.
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
|
|
87
Readme.md
87
Readme.md
|
@ -3,7 +3,7 @@
|
||||||
## Übersicht
|
## Übersicht
|
||||||
|
|
||||||
Als Backend für die StuVe IT Tools wird [Pocketbase](https://pocketbase.io)
|
Als Backend für die StuVe IT Tools wird [Pocketbase](https://pocketbase.io)
|
||||||
verwendet. Pocketbase ist ein Backend-as-a-Service, der auf SQLite basiert.
|
verwendet. Pocketbase ist ein self-hosted Backend-as-a-Service, der auf SQLite basiert.
|
||||||
|
|
||||||
## Was ist Pocketbase?
|
## Was ist Pocketbase?
|
||||||
|
|
||||||
|
@ -54,9 +54,9 @@ Funktionen für die einzelnen Tabellen und User erlaubt sind.
|
||||||
## Entwickeln
|
## Entwickeln
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone ...
|
git clone https://git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend.git
|
||||||
cd backend
|
cd stuve-it-backend
|
||||||
go run main.go serve # --debug=0 for no debug output
|
go run main/main.go serve # --debug=0 for no debug output
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -84,8 +84,10 @@ Die einzelnen custom go Packages sind weiter unten beschrieben.
|
||||||
│ ├── models.go # Structs für LDAP User und Group
|
│ ├── models.go # Structs für LDAP User und Group
|
||||||
│ ├── tables.go # Erstellt die benötigten Tabellen in der Datenbank (Users, Groups, Logs)
|
│ ├── tables.go # Erstellt die benötigten Tabellen in der Datenbank (Users, Groups, Logs)
|
||||||
│ └── ldapSync.go # Sync Funktionen (Sync Users, Groups, ...)
|
│ └── ldapSync.go # Sync Funktionen (Sync Users, Groups, ...)
|
||||||
└── ldapLogin # LDAP Login Package
|
├── ldapLogin # LDAP Login Package
|
||||||
└── main.go # Login Funktionen (Login Route)
|
│ └── main.go # Login Funktionen (Login Route)
|
||||||
|
└── qrApi # QR API Package
|
||||||
|
└── main.go # Api Route
|
||||||
```
|
```
|
||||||
|
|
||||||
## Custom Packages
|
## Custom Packages
|
||||||
|
@ -98,7 +100,46 @@ wurden einige Packages erstellt, die Pocketbase erweitern.
|
||||||
Das Logger Package ist ein custom Logger, der die Log-Nachrichten
|
Das Logger Package ist ein custom Logger, der die Log-Nachrichten
|
||||||
farblich in die Konsole schreibt.
|
farblich in die Konsole schreibt.
|
||||||
Diese Package ist sehr minimal gehalten und bietet nur die
|
Diese Package ist sehr minimal gehalten und bietet nur die
|
||||||
Funktionen `Info`, `Warn` und `Error` an.
|
Funktionen `Success`, `Info` und `Error` an.
|
||||||
|
|
||||||
|
#### Beispiel
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logger.LogInfoF("Info Message")
|
||||||
|
logger.LogErrorF("Warn Message")
|
||||||
|
logger.LogErrorF("Error Message")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### QR API
|
||||||
|
|
||||||
|
Das QR API Package ist ein einfaches Package, das eine API Route
|
||||||
|
zum Generieren von QR-Codes in SVG anbietet.
|
||||||
|
|
||||||
|
#### API Route
|
||||||
|
|
||||||
|
Das Package bietet die Route `GET /api/qr/v1` an, welche die folgenden
|
||||||
|
Parameter als json entgegennimmt:
|
||||||
|
|
||||||
|
| Parameter | Datentyp | Beschreibung |
|
||||||
|
|-------------------|-------------|------------------------------------------------------------------------------------------------|
|
||||||
|
| `data` | string | Der Text, der im QR-Code codiert werden soll (**required**) |
|
||||||
|
| `ecc` | string-enum | Der Error Correction Level des QR-Codes (`L`, `M` - default, `Q`, `H`) |
|
||||||
|
| `scale` | int | Der Scale des QR-Codes (default `1`) |
|
||||||
|
| `border` | int | Der Border des QR-Codes (default `1`), eine Border Uni ist die Größe von einem QR-Code 'pixel' |
|
||||||
|
| `color` | hex-string | Die Farbe des QR-Codes (default `#000000`) |
|
||||||
|
| `colorBackground` | hex-string | Die Hintergrundfarbe des QR-Codes (default `#FFFFFF`) |
|
||||||
|
|
||||||
|
#### Beispiel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X GET https://it.stuve.uni-ulm.de/api/qr/v1 -d '{"data": "https://stuve.uni-ulm.de"}'
|
||||||
|
```
|
||||||
|
|
||||||
### LDAP Sync
|
### LDAP Sync
|
||||||
|
|
||||||
|
@ -108,6 +149,36 @@ die LDAP Datenbank mit der Pocketbase Datenbank.
|
||||||
Dabei werden alle LDAP User und Gruppen in die Pocketbase Datenbank
|
Dabei werden alle LDAP User und Gruppen in die Pocketbase Datenbank
|
||||||
kopiert.
|
kopiert.
|
||||||
|
|
||||||
|
Dieses Package erfordert, dass alle Gruppen und User das Attribut `objectGUID` haben. Dies ist in der Regel bei Windows
|
||||||
|
ADs der Fall.
|
||||||
|
|
||||||
|
#### LDAP Attribute
|
||||||
|
|
||||||
|
Die folgenden LDAP Attribute werden in der Pocketbase Datenbank gespeichert.
|
||||||
|
|
||||||
|
##### User
|
||||||
|
|
||||||
|
| Attribut | Pocketbase Name | Beschreibung |
|
||||||
|
|----------------|--------------------------------------------|-------------------------------------------------------------------|
|
||||||
|
| objectGUID | id | Eindeutige ID des Users (nur die ersten 15 Zeichen werden genutzt |
|
||||||
|
| givenName | givenName | Vorname des Users |
|
||||||
|
| sn | sn | Nachname des Users |
|
||||||
|
| mail | email | E-Mail des Users |
|
||||||
|
| memberOf | memberOf (in Pocketbase eine SQL Relation) | Gruppenzugehörigkeit des Users |
|
||||||
|
| accountExpires | accountExpires (in Pocketbase UTC) | Account Ablaufdatum |
|
||||||
|
| dn | dn | Distinguished Name des Users |
|
||||||
|
| cn | cn & username | Common Name des Users |
|
||||||
|
|
||||||
|
##### Group
|
||||||
|
|
||||||
|
| Attribut | Pocketbase Name | Beschreibung |
|
||||||
|
|-------------|--------------------------------------------|--------------------------------------------------------------------|
|
||||||
|
| objectGUID | id | Eindeutige ID der Gruppe (nur die ersten 15 Zeichen werden genutzt |
|
||||||
|
| cn | cn | Common Name der Gruppe |
|
||||||
|
| dn | dn | Distinguished Name der Gruppe |
|
||||||
|
| description | description | Beschreibung der Gruppe |
|
||||||
|
| memberOf | memberOf (in Pocketbase eine SQL Relation) | Gruppenzugehörigkeit der Gruppe |
|
||||||
|
|
||||||
#### Konfiguration
|
#### Konfiguration
|
||||||
|
|
||||||
Das Package benötigt folgende ENV Variablen:
|
Das Package benötigt folgende ENV Variablen:
|
||||||
|
@ -147,7 +218,7 @@ noch nicht existiert. In diesem Fall wird die Beziehung erst beim
|
||||||
nächsten Sync erstellt. Falls eine `memberOf` Gruppe nicht gefunden wird,
|
nächsten Sync erstellt. Falls eine `memberOf` Gruppe nicht gefunden wird,
|
||||||
wird dies in der Konsole geloggt.
|
wird dies in der Konsole geloggt.
|
||||||
|
|
||||||
Für jede Synchronisation wird ein Log in der `ldap_sync_logs` Tabelle
|
Für jede Synchronisation wird ein Log-in der `ldap_sync_logs` Tabelle
|
||||||
erstellt.
|
erstellt.
|
||||||
|
|
||||||
Sowohl für die ldap_users als auch für die ldap_groups Tabelle wird
|
Sowohl für die ldap_users als auch für die ldap_groups Tabelle wird
|
||||||
|
|
13
go.mod
13
go.mod
|
@ -1,9 +1,14 @@
|
||||||
module gitlab.uni-ulm.de/stuve-it/it-tools/backend
|
module git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend
|
||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/fatih/color v1.15.0
|
||||||
github.com/go-ldap/ldap/v3 v3.4.6
|
github.com/go-ldap/ldap/v3 v3.4.6
|
||||||
|
github.com/google/uuid v1.3.1
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||||
|
github.com/pocketbase/dbx v1.10.1
|
||||||
github.com/pocketbase/pocketbase v0.19.0
|
github.com/pocketbase/pocketbase v0.19.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,7 +39,6 @@ require (
|
||||||
github.com/disintegration/imaging v1.6.2 // indirect
|
github.com/disintegration/imaging v1.6.2 // indirect
|
||||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/fatih/color v1.15.0 // indirect
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/ganigeorgiev/fexpr v0.3.0 // indirect
|
github.com/ganigeorgiev/fexpr v0.3.0 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||||
|
@ -43,23 +47,20 @@ require (
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/uuid v1.3.1 // indirect
|
|
||||||
github.com/google/wire v0.5.0 // indirect
|
github.com/google/wire v0.5.0 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/pocketbase/dbx v1.10.1 // indirect
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/spf13/cast v1.5.1 // indirect
|
github.com/spf13/cast v1.5.1 // indirect
|
||||||
github.com/spf13/cobra v1.7.0 // indirect
|
github.com/spf13/cobra v1.7.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -207,8 +207,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
|
|
|
@ -2,13 +2,13 @@ package ldapLogin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/ldapSync"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/apis"
|
"github.com/pocketbase/pocketbase/apis"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"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"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@ this file contains functions to sync ldap users and groups to the database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/forms"
|
"github.com/pocketbase/pocketbase/forms"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// upsertLDAPGroup This function creates / updates a record in the ldap groups table
|
// upsertLDAPGroup This function creates / updates a record in the ldap groups table
|
||||||
|
@ -27,8 +27,8 @@ func upsertLDAPGroup(app *pocketbase.PocketBase, ldapGroup *LDAPGroup) error {
|
||||||
// if record exists, update it
|
// if record exists, update it
|
||||||
if res, _ := app.Dao().FindFirstRecordByFilter(
|
if res, _ := app.Dao().FindFirstRecordByFilter(
|
||||||
ldapGroupsTableName,
|
ldapGroupsTableName,
|
||||||
"gidNumber = {:gidNumber}",
|
"id = {:id}",
|
||||||
dbx.Params{"gidNumber": ldapGroup.gidNumber},
|
dbx.Params{"id": ldapGroup.id},
|
||||||
); res != nil {
|
); res != nil {
|
||||||
record = res
|
record = res
|
||||||
} else { // if record does not exist, create it
|
} else { // if record does not exist, create it
|
||||||
|
@ -51,7 +51,7 @@ func upsertLDAPGroup(app *pocketbase.PocketBase, ldapGroup *LDAPGroup) error {
|
||||||
|
|
||||||
// load data
|
// load data
|
||||||
err = form.LoadData(map[string]any{
|
err = form.LoadData(map[string]any{
|
||||||
"gidNumber": ldapGroup.gidNumber,
|
"id": ldapGroup.id,
|
||||||
"cn": ldapGroup.cn,
|
"cn": ldapGroup.cn,
|
||||||
"dn": ldapGroup.dn,
|
"dn": ldapGroup.dn,
|
||||||
"description": ldapGroup.description,
|
"description": ldapGroup.description,
|
||||||
|
@ -83,8 +83,8 @@ func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
|
||||||
// if record exists, update it
|
// if record exists, update it
|
||||||
if res, _ := app.Dao().FindFirstRecordByFilter(
|
if res, _ := app.Dao().FindFirstRecordByFilter(
|
||||||
ldapUsersTableName,
|
ldapUsersTableName,
|
||||||
"uidNumber = {:uidNumber}",
|
"id = {:id}",
|
||||||
dbx.Params{"uidNumber": ldapUser.uidNumber},
|
dbx.Params{"id": ldapUser.id},
|
||||||
); res != nil {
|
); res != nil {
|
||||||
record = res
|
record = res
|
||||||
} else { // if record does not exist, create it
|
} else { // if record does not exist, create it
|
||||||
|
@ -111,7 +111,7 @@ func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record.Set("uidNumber", ldapUser.uidNumber)
|
record.Set("id", ldapUser.id)
|
||||||
record.Set("givenName", ldapUser.givenName)
|
record.Set("givenName", ldapUser.givenName)
|
||||||
record.Set("sn", ldapUser.sn)
|
record.Set("sn", ldapUser.sn)
|
||||||
record.Set("username", ldapUser.cn)
|
record.Set("username", ldapUser.cn)
|
||||||
|
|
|
@ -3,12 +3,13 @@ package ldapSync
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/forms"
|
"github.com/pocketbase/pocketbase/forms"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,21 @@ type SyncResult struct {
|
||||||
Errors []error
|
Errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getObjectGUID This function gets the objectGUID from an ldap entry
|
||||||
|
//
|
||||||
|
// since the objectGUID is a binary value, it is returned as a string (uuid)
|
||||||
|
func getObjectGUID(entry *ldap.Entry) (string, error) {
|
||||||
|
// get the objectGUID
|
||||||
|
var bytes = entry.GetRawAttributeValue("objectGUID")
|
||||||
|
|
||||||
|
// convert to uuid
|
||||||
|
u, err := uuid.FromBytes(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return u.String()[:15], nil // only use the first 15 characters of the uuid
|
||||||
|
}
|
||||||
|
|
||||||
// upsertLDAPGroup This function creates / updates a record in the ldap groups table
|
// upsertLDAPGroup This function creates / updates a record in the ldap groups table
|
||||||
//
|
//
|
||||||
// this function expects that the table ldapGroups already exists
|
// this function expects that the table ldapGroups already exists
|
||||||
|
@ -34,7 +50,7 @@ func syncLdapGroups(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResul
|
||||||
os.Getenv("LDAP_GROUP_BASE_DN"), // The base dn to search
|
os.Getenv("LDAP_GROUP_BASE_DN"), // The base dn to search
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
os.Getenv("LDAP_GROUP_FILTER"), // The filter to apply
|
os.Getenv("LDAP_GROUP_FILTER"), // The filter to apply
|
||||||
[]string{"gidNumber", "description", "dn", "cn", "msSFU30NisDomain", "memberOf"},
|
[]string{"description", "dn", "cn", "msSFU30NisDomain", "memberOf", "objectGUID"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,8 +62,15 @@ func syncLdapGroups(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResul
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range groupsFoundInLdap.Entries {
|
for _, entry := range groupsFoundInLdap.Entries {
|
||||||
|
|
||||||
|
var id, e = getObjectGUID(entry)
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
errors = append(errors, fmt.Errorf("unable to get objectGUID for group with dn: %s - %s", entry.DN, e))
|
||||||
|
}
|
||||||
|
|
||||||
err := upsertLDAPGroup(app, &LDAPGroup{
|
err := upsertLDAPGroup(app, &LDAPGroup{
|
||||||
gidNumber: entry.GetAttributeValue("gidNumber"),
|
id: id,
|
||||||
description: entry.GetAttributeValue("description"),
|
description: entry.GetAttributeValue("description"),
|
||||||
dn: entry.DN,
|
dn: entry.DN,
|
||||||
cn: entry.GetAttributeValue("cn"),
|
cn: entry.GetAttributeValue("cn"),
|
||||||
|
@ -115,7 +138,7 @@ func syncLdapUsers(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResult
|
||||||
os.Getenv("LDAP_BASE_DN"), // The base dn to search
|
os.Getenv("LDAP_BASE_DN"), // The base dn to search
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
os.Getenv("LDAP_USER_FILTER"), // The filter to apply
|
os.Getenv("LDAP_USER_FILTER"), // The filter to apply
|
||||||
[]string{"givenName", "sn", "accountExpires", "uidNumber", "mail", "dn", "cn", "msSFU30NisDomain", "memberOf"},
|
[]string{"givenName", "sn", "accountExpires", "mail", "dn", "cn", "msSFU30NisDomain", "memberOf", "objectGUID"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -127,11 +150,18 @@ func syncLdapUsers(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResult
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range usersFoundInLdap.Entries {
|
for _, entry := range usersFoundInLdap.Entries {
|
||||||
|
|
||||||
|
var id, e = getObjectGUID(entry)
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
errors = append(errors, fmt.Errorf("unable to get objectGUID for user with dn: %s - %s", entry.DN, e))
|
||||||
|
}
|
||||||
|
|
||||||
err := upsertLDAPUser(app, &LDAPUser{
|
err := upsertLDAPUser(app, &LDAPUser{
|
||||||
givenName: entry.GetAttributeValue("givenName"),
|
givenName: entry.GetAttributeValue("givenName"),
|
||||||
sn: entry.GetAttributeValue("sn"),
|
sn: entry.GetAttributeValue("sn"),
|
||||||
accountExpires: entry.GetAttributeValue("accountExpires"),
|
accountExpires: entry.GetAttributeValue("accountExpires"),
|
||||||
uidNumber: entry.GetAttributeValue("uidNumber"),
|
id: id,
|
||||||
mail: entry.GetAttributeValue("mail"),
|
mail: entry.GetAttributeValue("mail"),
|
||||||
dn: entry.DN,
|
dn: entry.DN,
|
||||||
cn: entry.GetAttributeValue("cn"),
|
cn: entry.GetAttributeValue("cn"),
|
||||||
|
|
|
@ -5,9 +5,9 @@ package ldapSync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/tools/cron"
|
"github.com/pocketbase/pocketbase/tools/cron"
|
||||||
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -88,7 +88,7 @@ func InitLdapSync(app *pocketbase.PocketBase) error {
|
||||||
|
|
||||||
ldapSyncSchedule := os.Getenv("LDAP_SYNC_SCHEDULE")
|
ldapSyncSchedule := os.Getenv("LDAP_SYNC_SCHEDULE")
|
||||||
|
|
||||||
// syncs ldap every 2 minutes
|
// syncs ldap - interval specified in the LDAP_SYNC_SCHEDULE env variable
|
||||||
scheduler.MustAdd("ldapSync", ldapSyncSchedule, func() {
|
scheduler.MustAdd("ldapSync", ldapSyncSchedule, func() {
|
||||||
logger.LogInfoF("syncing LDAP ...")
|
logger.LogInfoF("syncing LDAP ...")
|
||||||
syncLdap(app)
|
syncLdap(app)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package ldapSync
|
package ldapSync
|
||||||
|
|
||||||
type LDAPUser struct {
|
type LDAPUser struct {
|
||||||
|
id string
|
||||||
givenName string
|
givenName string
|
||||||
sn string
|
sn string
|
||||||
accountExpires string
|
accountExpires string
|
||||||
uidNumber string
|
|
||||||
mail string
|
mail string
|
||||||
|
|
||||||
dn string
|
dn string
|
||||||
|
@ -14,7 +14,7 @@ type LDAPUser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPGroup struct {
|
type LDAPGroup struct {
|
||||||
gidNumber string
|
id string
|
||||||
description string
|
description string
|
||||||
|
|
||||||
dn string
|
dn string
|
||||||
|
|
|
@ -33,13 +33,6 @@ func createLDAPGroupsTable(app *pocketbase.PocketBase) error {
|
||||||
form.UpdateRule = nil
|
form.UpdateRule = nil
|
||||||
form.DeleteRule = nil
|
form.DeleteRule = nil
|
||||||
|
|
||||||
// add group ID field
|
|
||||||
form.Schema.AddField(&schema.SchemaField{
|
|
||||||
Name: "gidNumber",
|
|
||||||
Type: schema.FieldTypeText,
|
|
||||||
Required: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// add description field
|
// add description field
|
||||||
form.Schema.AddField(&schema.SchemaField{
|
form.Schema.AddField(&schema.SchemaField{
|
||||||
Name: "description",
|
Name: "description",
|
||||||
|
@ -64,7 +57,7 @@ func createLDAPGroupsTable(app *pocketbase.PocketBase) error {
|
||||||
|
|
||||||
// create index on cn
|
// create index on cn
|
||||||
form.Indexes = types.JsonArray[string]{
|
form.Indexes = types.JsonArray[string]{
|
||||||
"CREATE UNIQUE INDEX idx_ldapGroups ON " + ldapGroupsTableName + " (cn, gidNumber, dn)",
|
"CREATE UNIQUE INDEX idx_ldapGroups ON " + ldapGroupsTableName + " (cn, dn)",
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate and submit (internally it calls app.Dao().SaveCollection(collection) in a transaction)
|
// validate and submit (internally it calls app.Dao().SaveCollection(collection) in a transaction)
|
||||||
|
@ -144,13 +137,6 @@ func createLDAPUsersTable(app *pocketbase.PocketBase) error {
|
||||||
Required: true,
|
Required: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// add uidNumber field
|
|
||||||
form.Schema.AddField(&schema.SchemaField{
|
|
||||||
Name: "uidNumber",
|
|
||||||
Type: schema.FieldTypeText,
|
|
||||||
Required: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// add surname field
|
// add surname field
|
||||||
form.Schema.AddField(&schema.SchemaField{
|
form.Schema.AddField(&schema.SchemaField{
|
||||||
Name: "sn",
|
Name: "sn",
|
||||||
|
@ -185,7 +171,7 @@ func createLDAPUsersTable(app *pocketbase.PocketBase) error {
|
||||||
|
|
||||||
// create index on username
|
// create index on username
|
||||||
form.Indexes = types.JsonArray[string]{
|
form.Indexes = types.JsonArray[string]{
|
||||||
"CREATE UNIQUE INDEX idx_ldapUsers ON " + ldapGroupsTableName + " (cn, uidNumber, dn)",
|
"CREATE UNIQUE INDEX idx_ldapUsers ON " + ldapGroupsTableName + " (cn, dn)",
|
||||||
}
|
}
|
||||||
|
|
||||||
return form.Submit()
|
return form.Submit()
|
||||||
|
|
21
main/main.go
21
main/main.go
|
@ -1,19 +1,27 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/ldapLogin"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/ldapSync"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/qrApi"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/ldapLogin"
|
|
||||||
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/ldapSync"
|
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// load env
|
// load env
|
||||||
godotenv.Load(".env.local")
|
err := godotenv.Load(".env.local")
|
||||||
godotenv.Load(".env")
|
if err != nil {
|
||||||
|
logger.LogInfoF("The file '.env.local' could not be loaded.")
|
||||||
|
}
|
||||||
|
err = godotenv.Load(".env")
|
||||||
|
if err != nil {
|
||||||
|
logger.LogInfoF("The file '.env' could not be loaded.")
|
||||||
|
}
|
||||||
|
|
||||||
// create app
|
// create app
|
||||||
app := pocketbase.New()
|
app := pocketbase.New()
|
||||||
|
@ -28,6 +36,11 @@ func main() {
|
||||||
return ldapLogin.InitLDAPLogin(app, e)
|
return ldapLogin.InitLDAPLogin(app, e)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// setup qr api
|
||||||
|
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||||
|
return qrApi.InitQRApi(app, e)
|
||||||
|
})
|
||||||
|
|
||||||
// start app
|
// start app
|
||||||
if err := app.Start(); err != nil {
|
if err := app.Start(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
package qrApi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
goqr "git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/go-qr"
|
||||||
|
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
|
||||||
|
"github.com/labstack/echo/v5"
|
||||||
|
"github.com/pocketbase/pocketbase"
|
||||||
|
"github.com/pocketbase/pocketbase/apis"
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitQRApi initializes ar code api endpoint
|
||||||
|
//
|
||||||
|
// this endpoint can be used to create qr codes
|
||||||
|
// GET /api/qr/v1
|
||||||
|
//
|
||||||
|
// the endpoint expects the following request data:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "data": "what to be encoded",
|
||||||
|
// "ecc": "error correction level ('L', 'M' (default), 'Q', 'H')",
|
||||||
|
// "scale": "scale of qr code (default 1), must be greater than 0",
|
||||||
|
// "border": "border of qr code (default 1), must be equal or greater than 0, one is equal to one qr-code 'pixel'"
|
||||||
|
// "color": "color of qr code (default #000000)"
|
||||||
|
// "colorBackground": "background color of qr code (default #FFFFFF)",
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if the user is authenticated successfully the endpoint returns and apis.RecordAuthResponse
|
||||||
|
func InitQRApi(app *pocketbase.PocketBase, e *core.ServeEvent) error {
|
||||||
|
|
||||||
|
// add endpoint to app
|
||||||
|
logger.LogInfoF("Adding QR API Endpoint")
|
||||||
|
|
||||||
|
e.Router.GET("/api/qr/v1", func(c echo.Context) error {
|
||||||
|
|
||||||
|
// get data from request
|
||||||
|
data := struct {
|
||||||
|
Data string `json:"data"`
|
||||||
|
ECC string `json:"ecc"`
|
||||||
|
Scale int `json:"scale"`
|
||||||
|
Border int `json:"border"`
|
||||||
|
ColorDark string `json:"color"`
|
||||||
|
ColorLight string `json:"colorBackground"`
|
||||||
|
}{}
|
||||||
|
if err := c.Bind(&data); err != nil {
|
||||||
|
return apis.NewBadRequestError("Failed to read request data", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get correct error correction level
|
||||||
|
var errCorLvl goqr.Ecc
|
||||||
|
switch data.ECC {
|
||||||
|
case "L":
|
||||||
|
errCorLvl = goqr.Low
|
||||||
|
case "M":
|
||||||
|
errCorLvl = goqr.Medium
|
||||||
|
case "Q":
|
||||||
|
errCorLvl = goqr.Quartile
|
||||||
|
case "H":
|
||||||
|
errCorLvl = goqr.High
|
||||||
|
default:
|
||||||
|
errCorLvl = goqr.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode data to qr code
|
||||||
|
qrCode, err := goqr.EncodeText(data.Data, errCorLvl)
|
||||||
|
if err != nil {
|
||||||
|
return apis.NewBadRequestError("Failed to encode data to qr code", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default scale and border
|
||||||
|
scale := data.Scale
|
||||||
|
border := data.Border
|
||||||
|
if scale == 0 {
|
||||||
|
scale = 1
|
||||||
|
}
|
||||||
|
if border == 0 {
|
||||||
|
border = 1
|
||||||
|
}
|
||||||
|
config := goqr.NewQrCodeImgConfig(scale, border)
|
||||||
|
|
||||||
|
// create new buffer and writer
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
writer := io.Writer(&buffer)
|
||||||
|
|
||||||
|
// set default colors
|
||||||
|
colorLight := data.ColorLight
|
||||||
|
colorDark := data.ColorDark
|
||||||
|
if colorLight == "" {
|
||||||
|
colorLight = "#FFFFFF"
|
||||||
|
}
|
||||||
|
if colorDark == "" {
|
||||||
|
colorDark = "#000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
// write qr code to buffer
|
||||||
|
err = qrCode.WriteAsSVG(config, writer, colorLight, colorDark)
|
||||||
|
|
||||||
|
// return error if writing qr code to buffer fails
|
||||||
|
if err != nil {
|
||||||
|
logger.LogErrorF("Failed to write QR Code: %e", err)
|
||||||
|
return apis.NewBadRequestError("Failed to write QR Code", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return qr code as svg
|
||||||
|
return c.Stream(200, "image/svg+xml", io.Reader(&buffer))
|
||||||
|
}, apis.ActivityLogger(app))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue