5 Commits

Author SHA1 Message Date
Arnaud Delcasse
b425499bbf fix: base distroless sans nonroot pour port 80 2026-03-03 12:04:09 +01:00
Arnaud Delcasse
6112f6f7aa fix: assets web Dex dans /web 2026-03-03 11:16:12 +01:00
Arnaud Delcasse
a7ecc4f82e fix: set workdir as / in dockerfile 2026-03-03 05:55:35 +01:00
Arnaud Delcasse
a40a7e2712 fix: bump golang to 1.25, remove integration tests 2026-03-03 00:02:19 +01:00
Arnaud Delcasse
1787a90909 Migrate CI to GitLab, clean up Dockerfile and repo 2026-03-02 20:41:59 +01:00
11 changed files with 42 additions and 532 deletions

View File

@@ -1,82 +0,0 @@
name: Build and Push Docker Image
on:
push:
tags:
- '*'
branches:
- main
- dev
jobs:
build_and_push:
runs-on: ubuntu-latest
steps:
- name: Install Docker
run: |
apt-get update
apt-get install -y docker.io
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set Kubernetes Context
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{secrets.buildx_kubeconfig}}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: kubernetes
driver-opts: |
namespace=gitea
- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: git.coopgo.io
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Extract metadata (tags, labels) for Docker image
id: metadata
uses: docker/metadata-action@v3
with:
images: git.coopgo.io/${{gitea.repository}}
tags: |
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
flavor: |
latest=auto
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ steps.metadata.outputs.tags }}
build-args: |
ACCESS_TOKEN_USR=${{gitea.actor}}
ACCESS_TOKEN_PWD=${{gitea.token}}
# BUILD WITH KANIKO
# - name: Kaniko build and push
# uses: aevea/action-kaniko@master
# with:
# build_file: Dockerfile
# registry: git.coopgo.io
# username: ${{secrets.registry_user}}
# password: ${{secrets.registry_token}}
# image: ${{gitea.repository}}
# tag: ${{gitea.ref_name}}
# cache: true
# cache_registry: git.coopgo.io/${{gitea.repository}}/cache
# extra-args: |
# ACCESS_TOKEN_USR=${{gitea.actor}}
# ACCESS_TOKEN_PWD=${{gitea.token}}

2
.gitignore vendored
View File

@@ -1,4 +1,6 @@
config.yaml config.yaml
.vscode .vscode
.idea/
__debug_bin __debug_bin
mobility-accounts mobility-accounts
build/

31
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,31 @@
stages:
- test
- publish
variables:
GOLANG_VERSION: "1.25"
default:
image: golang:${GOLANG_VERSION}
test:
stage: test
script:
- go test ./...
docker:
stage: publish
image: docker:latest
services:
- docker:dind
variables:
DOCKER_BUILDKIT: "1"
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
rules:
- if: $CI_COMMIT_TAG

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="Go" enabled="true" />
</module>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1,28 +1,17 @@
FROM golang:alpine as builder FROM golang:alpine AS builder
ARG ACCESS_TOKEN_USR="nothing" WORKDIR /app
ARG ACCESS_TOKEN_PWD="nothing"
RUN apk add --no-cache ca-certificates tzdata
WORKDIR /
# Create a netrc file using the credentials specified using --build-arg
RUN printf "machine git.coopgo.io\n\
login ${ACCESS_TOKEN_USR}\n\
password ${ACCESS_TOKEN_PWD}\n"\
>> ~/.netrc
RUN chmod 600 ~/.netrc
COPY . . COPY . .
RUN go mod download && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /server RUN CGO_ENABLED=0 go build -o /server
FROM gcr.io/distroless/static
WORKDIR /
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /server / COPY --from=builder /server /
COPY --from=builder /oidc-provider/web /oidc-provider/web COPY --from=builder /app/oidc-provider/web /web
EXPOSE 8080 EXPOSE 8080
EXPOSE 80 EXPOSE 80

View File

@@ -124,11 +124,9 @@ func createNamespaceDexServer(handler *handlers.MobilityAccountsHandler, stor st
// Determine web config // Determine web config
webCfg := server.WebConfig{ webCfg := server.WebConfig{
Dir: "/web",
Issuer: nsName, Issuer: nsName,
} }
if nsCfg.TemplatesDir != "" {
webCfg.Dir = nsCfg.TemplatesDir
}
// Dex v2.42 manages signing keys internally via storage. // Dex v2.42 manages signing keys internally via storage.
dexServer, err := server.NewServer(ctx, server.Config{ dexServer, err := server.NewServer(ctx, server.Config{

View File

@@ -1,118 +0,0 @@
package storage
import (
"testing"
"github.com/google/uuid"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
var cfg2 *viper.Viper
func init() {
cfg2 = viper.New()
cfg2.SetDefault("storage.db.mongodb.host", "localhost")
cfg2.SetDefault("storage.db.mongodb.port", "27017")
cfg2.SetDefault("storage.db.mongodb.user", "mongodb")
cfg2.SetDefault("storage.db.mongodb.db_name", "mobilityaccounts_tests")
cfg2.SetDefault("storage.db.mongodb.sslmode", "disable")
cfg2.SetDefault("storage.db.mongodb.collections.users", "users")
cfg2.SetConfigName("config") // Define values in config.yaml
cfg2.AddConfigPath(".")
cfg2.ReadInConfig()
}
func TestMongoDBStorage_CreateAndGetAccount(t *testing.T) {
db, err := NewMongoDBStorage(cfg2)
require.NoError(t, err)
err = db.CreateAccount(account1)
require.NoError(t, err)
result, err := db.GetAccount(account1.ID)
require.NoError(t, err)
require.Equal(t, &account1, result)
}
func TestMongoDBStorage_CreateAndGetAccountNoAuth(t *testing.T) {
db, err := NewMongoDBStorage(cfg2)
require.NoError(t, err)
err = db.CreateAccount(account3)
require.NoError(t, err)
result, err := db.GetAccount(account3.ID)
require.NoError(t, err)
require.Equal(t, &account3, result)
}
func TestMongoDBStorage_UpdateAccount(t *testing.T) {
db, err := NewMongoDBStorage(cfg2)
require.NoError(t, err)
err = db.CreateAccount(account2)
require.NoError(t, err)
modified := account2
modified.Authentication.Local.Email = Ptr("modifiedtest@test.com")
modified.Data["key1"] = "modeifiedvalue"
modified.Data["addedkey"] = "addedvalue"
modified.Metadata["addedmetadatakey"] = "addedmetadatavalue"
err = db.UpdateAccount(modified)
require.NoError(t, err)
result, err := db.GetAccount(account2.ID)
require.NoError(t, err)
require.Equal(t, &modified, result)
}
func TestMongoDBStorage_LocalAuthentication(t *testing.T) {
db, err := NewMongoDBStorage(cfg2)
require.NoError(t, err)
_, err = db.LocalAuthentication(account1.Namespace, account1.Authentication.Local.Username, nil, nil)
require.NoError(t, err)
_, err = db.LocalAuthentication(account1.Namespace, nil, account1.Authentication.Local.Email, nil)
require.NoError(t, err)
_, err = db.LocalAuthentication(account1.Namespace, nil, nil, account1.Authentication.Local.PhoneNumber)
require.NoError(t, err)
}
func TestMongoDBStorage_GetAccounts(t *testing.T) {
db, err := NewMongoDBStorage(cfg2)
require.NoError(t, err)
accounts, err := db.GetAccounts([]string{account1.Namespace, account3.Namespace})
require.NoError(t, err)
for _, account := range accounts {
require.Contains(t, []string{account1.Namespace, account3.Namespace}, account.Namespace)
}
}
func TestMongoDBsqlStorage_GetAccountsByIds(t *testing.T) {
db, err := NewMongoDBStorage(cfg2)
require.NoError(t, err)
accounts, err := db.GetAccountsByIds([]string{account2.ID, account3.ID})
require.NoError(t, err)
for _, account := range accounts {
require.Contains(t, []string{account2.ID, account3.ID}, account.ID)
}
}
func TestMongoDBStorage_CreateAlreadyExistingCredentials(t *testing.T) {
db, err := NewMongoDBStorage(cfg2)
require.NoError(t, err)
modified := account1
modified.ID = uuid.NewString() // Change the ID to make as if it was a new one
err = db.CreateAccount(modified)
require.Error(t, err)
}

View File

@@ -1,225 +0,0 @@
package storage
import (
"context"
"fmt"
"reflect"
"testing"
"github.com/google/uuid"
_ "github.com/lib/pq"
"github.com/spf13/viper"
)
var cfg *viper.Viper
func init() {
cfg = viper.New()
cfg.SetDefault("storage.db.psql.host", "localhost")
cfg.SetDefault("storage.db.psql.port", "5432")
cfg.SetDefault("storage.db.psql.user", "postgres")
cfg.SetDefault("storage.db.psql.password", "postgres")
cfg.SetDefault("storage.db.psql.dbname", "coopgo_platform")
cfg.SetDefault("storage.db.psql.sslmode", "disable")
cfg.SetDefault("storage.db.psql.schema", "mobilityaccounts")
cfg.SetDefault("storage.db.psql.tables.accounts", "accounts")
cfg.SetDefault("storage.db.psql.tables.accounts_auth_local", "accounts_auth_local")
cfg.SetConfigName("config") // Override default values in a config.yaml file within this directory
cfg.AddConfigPath(".")
cfg.ReadInConfig()
}
func TestPostgresqlStorage_Initialize(t *testing.T) {
storage, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("error creating new PostgreSQL storage: %v", err)
}
defer storage.DbConnection.Close()
err = storage.Migrate()
if err != nil {
t.Errorf("database migration issue: %v", err)
return
}
tx, err := storage.DbConnection.BeginTx(context.Background(), nil)
if err != nil {
t.Errorf("transaction issue: %v", err)
return
}
defer tx.Rollback()
_, err = tx.Exec(fmt.Sprintf("DELETE FROM %s;", storage.Tables["accounts_auth_local"]))
if err != nil {
t.Errorf("delete accounts table issue: %v", err)
return
}
_, err = tx.Exec(fmt.Sprintf("DELETE FROM %s;", storage.Tables["accounts"]))
if err != nil {
t.Errorf("delete accounts table issue: %v", err)
return
}
if err = tx.Commit(); err != nil {
t.Errorf("commit transaction issue: %v", err)
return
}
}
func TestPostgresqlStorage_CreateAndGetAccount(t *testing.T) {
db, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("failed to create new psql connection")
}
err = db.CreateAccount(account1)
if err != nil {
t.Errorf("Failed to create account : %s", err)
return
}
result, err := db.GetAccount(account1.ID)
if err != nil {
t.Errorf("Failed to get account : %s", err)
return
}
if !reflect.DeepEqual(&account1, result) {
t.Errorf("The received account is not the same as expected\nSaved Account : %v\nRetrieved Account : %v", &account1, result)
}
}
func TestPostgresqlStorage_CreateAndGetAccountNoAuth(t *testing.T) {
db, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("failed to create new psql connection")
}
err = db.CreateAccount(account3)
if err != nil {
t.Errorf("Failed to create account : %s", err)
return
}
result, err := db.GetAccount(account3.ID)
if err != nil {
t.Errorf("Failed to get account : %s", err)
return
}
if !reflect.DeepEqual(&account3, result) {
t.Errorf("The received account is not the same as expected\nSaved Account : %v\nRetrieved Account : %v", &account3, result)
}
}
func TestPostgresqlStorage_UpdateAccount(t *testing.T) {
db, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("failed to create new psql connection")
}
err = db.CreateAccount(account2)
if err != nil {
t.Errorf("Failed to create account : %s", err)
return
}
modified := account2
modified.Authentication.Local.Email = Ptr("modifiedtest@test.com")
modified.Data["key1"] = "modeifiedvalue"
modified.Data["addedkey"] = "addedvalue"
modified.Metadata["addedmetadatakey"] = "addedmetadatavalue"
err = db.UpdateAccount(modified)
if err != nil {
t.Errorf("failed updating account : %s", err)
}
result, err := db.GetAccount(account2.ID)
if err != nil {
t.Errorf("Failed to get account : %s", err)
return
}
if !reflect.DeepEqual(&modified, result) {
t.Errorf("The received account is not the same as expected\nSaved Account : %v\nRetrieved Account : %v", &modified, result)
}
}
func TestPostgresqlStorage_LocalAuthentication(t *testing.T) {
db, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("failed to create new psql connection")
}
_, err = db.LocalAuthentication(account1.Namespace, account1.Authentication.Local.Username, nil, nil)
if err != nil {
t.Errorf("Failed LocalAuthentication based on username and namespace : %s", err)
}
_, err = db.LocalAuthentication(account1.Namespace, nil, account1.Authentication.Local.Email, nil)
if err != nil {
t.Errorf("Failed LocalAuthentication based on email and namespace :\n Namespace: %s\n Email: %s\nError: %s", account1.Namespace, *account1.Authentication.Local.Email, err)
}
_, err = db.LocalAuthentication(account1.Namespace, nil, nil, account1.Authentication.Local.PhoneNumber)
if err != nil {
t.Errorf("Failed LocalAuthentication based on phone number and namespace :\n Namespace: %s\n Phone number: %s\nError: %s", account1.Namespace, *account1.Authentication.Local.PhoneNumber, err)
}
}
func TestPostgresqlStorage_GetAccounts(t *testing.T) {
db, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("failed to create new psql connection")
}
accounts, err := db.GetAccounts([]string{account1.Namespace, account3.Namespace})
if err != nil {
t.Errorf("Failed : %s", err)
return
}
for _, account := range accounts {
if account.Namespace != account1.Namespace && account.Namespace != account3.Namespace {
t.Errorf("This namespace was not requested : %s", account.Namespace)
}
}
}
func TestPostgresqlStorage_GetAccountsByIds(t *testing.T) {
db, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("failed to create new psql connection")
return
}
accounts, err := db.GetAccountsByIds([]string{account2.ID, account3.ID})
if err != nil {
t.Errorf("Failed to get account by ID : %s", err)
return
}
found2 := false
found3 := false
for _, account := range accounts {
if account.ID == account2.ID {
found2 = true
} else if account.ID == account3.ID {
found3 = true
} else {
t.Errorf("This id was not requested : %s", account.ID)
}
}
if !found2 {
t.Errorf("account id not found for account2 : %s", account2.ID)
}
if !found3 {
t.Errorf("account id not found for account3 : %s", account3.ID)
}
}
func TestPostgresqlStorage_CreateAlreadyExistingCredentials(t *testing.T) {
db, err := NewPostgresqlStorage(cfg)
if err != nil {
t.Errorf("failed to create new psql connection")
return
}
modified := account1
modified.ID = uuid.NewString() // Change the ID to make as if it was a new one
err = db.CreateAccount(modified)
if err == nil {
t.Errorf("account should not be created : unique index violated !")
return
}
}

View File

@@ -1,67 +0,0 @@
package storage
import "github.com/google/uuid"
var account1 = Account{
ID: uuid.NewString(),
Namespace: "namespace",
Authentication: AccountAuth{
Local: &LocalAuth{
Username: Ptr("test"),
Password: "hashedpassword",
Email: Ptr("test@test.com"),
EmailValidation: &Validation{
Validated: true,
ValidationCode: "",
},
PhoneNumber: Ptr("+3312345678"),
PhoneNumberValidation: &Validation{
Validated: true,
ValidationCode: "",
},
},
},
Data: map[string]any{
"key1": "value1",
"key2": "value2",
},
Metadata: map[string]any{
"key1": "value1",
"key2": "value2",
},
}
var account2 = Account{
ID: uuid.NewString(),
Namespace: "test",
Authentication: AccountAuth{
Local: &LocalAuth{
Username: Ptr("test2"),
Password: "hashedpassword",
},
},
Data: map[string]any{
"key1": "value3",
"key2": "value4",
},
Metadata: map[string]any{
"key1": "value5",
"key2": "value6",
},
}
var account3 = Account{
ID: uuid.NewString(),
Namespace: "other_namespace",
Authentication: AccountAuth{
Local: nil,
},
Data: map[string]any{
"key1": "value3",
"key2": "value4",
},
Metadata: map[string]any{
"key1": "value5",
"key2": "value6",
},
}