Skip to content
Draft
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ require (
github.com/onsi/gomega v1.39.1
github.com/open-policy-agent/opa v1.14.1
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310095109-64a5650c97e2
github.com/opencloud-eu/reva/v2 v2.42.5
github.com/opensearch-project/opensearch-go/v4 v4.6.0
github.com/orcaman/concurrent-map v1.0.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -963,8 +963,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIftlX03Bzfbujhp9B54FbgER0VBDWJi/w8RBxJlzxU=
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068 h1:i09YEVYbiUBMhxyak93REn/ZJOTRhAN4I3PXp2nCXgU=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310095109-64a5650c97e2 h1:DgeiMlGRDiUS5eitDRznhXH+5hicM9pG/ufOD6+9TD0=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310095109-64a5650c97e2/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/reva/v2 v2.42.5 h1:Srhk8++3zJe3KA1u2Vqh4VbmljbblF75DR7t4HW0Kxw=
github.com/opencloud-eu/reva/v2 v2.42.5/go.mod h1:U3UaHyAQcutavXyLaLE3UVY5n6t2pRAN9uv09n69lwI=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
Expand Down
10 changes: 10 additions & 0 deletions services/invitations/pkg/command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"github.com/opencloud-eu/opencloud/services/invitations/pkg/server/debug"
"github.com/opencloud-eu/opencloud/services/invitations/pkg/server/http"
"github.com/opencloud-eu/opencloud/services/invitations/pkg/service/v0"
"github.com/opencloud-eu/reva/v2/pkg/store"
microstore "go-micro.dev/v4/store"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -45,12 +47,20 @@ func Server(cfg *config.Config) *cobra.Command {
metrics := metrics.New(metrics.Logger(logger))
metrics.BuildInfo.WithLabelValues(version.GetString()).Set(1)

invitiationPersistanceStore := store.Create(
store.Store(cfg.Persistance.Store),
microstore.Nodes(cfg.Persistance.Nodes...),
microstore.Database(cfg.Persistance.Database),
store.Authentication(cfg.Persistance.AuthUsername, cfg.Persistance.AuthPassword),
)

gr := runner.NewGroup()
{

svc, err := service.New(
service.Logger(logger),
service.Config(cfg),
service.WithPersistance(&invitiationPersistanceStore),
// service.WithRelationProviders(relationProviders),
)
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions services/invitations/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"context"
"time"

"github.com/opencloud-eu/opencloud/pkg/shared"
)
Expand All @@ -17,10 +18,16 @@ type Config struct {

HTTP HTTP `yaml:"http"`

UseBackEndForInvitations bool `yaml:"use_backend_for_invitations" env:"INVITATIONS_USE_BACKEND_FOR_INVITATIONS" desc:"Use the backend for invitations." introductionVersion:"1.0.0"`

Keycloak Keycloak `yaml:"keycloak"`
TokenManager *TokenManager `yaml:"token_manager"`

Invitation Invitation `yaml:"invitation"`

Context context.Context `yaml:"-"`

Persistance Persistance `yaml:"persistance"`
}

// Keycloak configuration
Expand All @@ -32,3 +39,16 @@ type Keycloak struct {
UserRealm string `yaml:"user_realm" env:"OC_KEYCLOAK_USER_REALM;INVITATIONS_KEYCLOAK_USER_REALM" desc:"The realm users are defined." introductionVersion:"1.0.0"`
InsecureSkipVerify bool `yaml:"insecure_skip_verify" env:"OC_KEYCLOAK_INSECURE_SKIP_VERIFY;INVITATIONS_KEYCLOAK_INSECURE_SKIP_VERIFY" desc:"Disable TLS certificate validation for Keycloak connections. Do not set this in production environments." introductionVersion:"1.0.0"`
}

type Invitation struct {
MaxTTL time.Duration `yaml:"max_ttl" env:"INVITATIONS_MAX_TTL" desc:"The maximum time to live for an invitation. (defaults to 30 days)" introductionVersion:"%%NEXT%%"`
}

type Persistance struct {
Store string `yaml:"store" env:"INVITATIONS_PERSISTANCE_STORE" desc:"The type of the cache store. Supported values are: 'memory', 'nats-js-kv'. See the text description for details." introductionVersion:"%%NEXT%%""`
Nodes []string `yaml:"addresses" env:"INVITATIONS_PERSISTANCE_NODES" desc:"A list of nodes to access the configured store. This has no effect when 'memory' store is configured. Note that the behaviour how nodes are used is dependent on the library of the configured store. See the Environment Variable Types description for more details." introductionVersion:"%%NEXT%%"`
Database string `yaml:"database" env:"INVITATIONS_PERSISTANCE_DATABASE" desc:"The database name the configured store should use." introductionVersion:"%%NEXT%%"`
Table string `yaml:"table" env:"INVITATIONS_PERSISTANCE_TABLE" desc:"The database table the store should use." introductionVersion:"%%NEXT%%"`
AuthUsername string `yaml:"username" env:"INVITATIONS_PERSISTANCE_AUTH_USERNAME" desc:"The username to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured." introductionVersion:"%%NEXT%%"`
AuthPassword string `yaml:"password" env:"INVITATIONS_PERSISTANCE_PASSWORD" desc:"The password to authenticate with the cache. Only applies when store type 'nats-js-kv' is configured." introductionVersion:"%%NEXT%%"`
}
13 changes: 13 additions & 0 deletions services/invitations/pkg/config/defaults/defaultconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package defaults

import (
"strings"
"time"

"github.com/opencloud-eu/opencloud/services/invitations/pkg/config"
)
Expand Down Expand Up @@ -32,13 +33,25 @@ func DefaultConfig() *config.Config {
Service: config.Service{
Name: "invitations",
},
UseBackEndForInvitations: false,
Keycloak: config.Keycloak{
BasePath: "",
ClientID: "",
ClientSecret: "",
ClientRealm: "",
UserRealm: "",
},
Invitation: config.Invitation{
MaxTTL: 30 * 24 * time.Hour,
},
Persistance: config.Persistance{
Store: "memory",
Nodes: []string{},
Database: "",
Table: "",
AuthUsername: "",
AuthPassword: "",
},
}
}

Expand Down
44 changes: 42 additions & 2 deletions services/invitations/pkg/server/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ func Server(opts ...Option) (ohttp.Service, error) {
))

mux.Route(options.Config.HTTP.Root, func(r chi.Router) {
r.Post("/invitations", InvitationHandler(service))
r.Post("/invitations", InvitationPostHandler(service))
r.Get("/invitations", InvitationListGetHandler(service))
r.Get("/invitations/{id}", InvitationGetHandler(service))
})

err = micro.RegisterHandler(svc.Server(), mux)
Expand All @@ -83,7 +85,7 @@ func Server(opts ...Option) (ohttp.Service, error) {
return svc, nil
}

func InvitationHandler(service svc.Service) func(w http.ResponseWriter, r *http.Request) {
func InvitationPostHandler(service svc.Service) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

Expand All @@ -105,3 +107,41 @@ func InvitationHandler(service svc.Service) func(w http.ResponseWriter, r *http.
render.JSON(w, r, res)
}
}

func InvitationGetHandler(service svc.Service) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

id := chi.URLParam(r, "id")
if id == "" {
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing invitation ID")
return
}

res, err := service.Get(ctx, id)
if err != nil {
render.Status(r, http.StatusInternalServerError)
render.PlainText(w, r, err.Error())
return
}

render.Status(r, http.StatusOK)
render.JSON(w, r, res)
}
}

func InvitationListGetHandler(service svc.Service) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

res, err := service.List(ctx)
if err != nil {
render.Status(r, http.StatusInternalServerError)
render.PlainText(w, r, err.Error())
return
}

render.Status(r, http.StatusOK)
render.JSON(w, r, res)
}
}
4 changes: 4 additions & 0 deletions services/invitations/pkg/service/v0/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ var (
ErrBadRequest = errors.New("bad request")
ErrMissingEmail = errors.New("missing email address")
ErrBackend = errors.New("backend error")

ErrSerialization = errors.New("serialization error")
ErrPersistence = errors.New("persistence error")
ErrUnauthorized = errors.New("unauthorized")
)
30 changes: 30 additions & 0 deletions services/invitations/pkg/service/v0/instrument.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,36 @@ type instrument struct {
metrics *metrics.Metrics
}

func (i instrument) List(ctx context.Context, userId string) ([]*invitations.Invitation, error) {
timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {
us := v * 1000000

i.metrics.Latency.WithLabelValues().Observe(us)
i.metrics.Duration.WithLabelValues().Observe(v)
}))

defer timer.ObserveDuration()

i.metrics.Counter.WithLabelValues().Inc()

return i.next.List(ctx, userId)
}

func (i instrument) GetByInvitedEmail(ctx context.Context, email string) (*invitations.Invitation, error) {
timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {
us := v * 1000000

i.metrics.Latency.WithLabelValues().Observe(us)
i.metrics.Duration.WithLabelValues().Observe(v)
}))

defer timer.ObserveDuration()

i.metrics.Counter.WithLabelValues().Inc()

return i.next.GetByInvitedEmail(ctx, email)
}

// Invite implements the Service interface.
func (i instrument) Invite(ctx context.Context, invitation *invitations.Invitation) (*invitations.Invitation, error) {
timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {
Expand Down
16 changes: 16 additions & 0 deletions services/invitations/pkg/service/v0/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ type logging struct {
logger log.Logger
}

func (l logging) List(ctx context.Context, userId string) ([]*invitations.Invitation, error) {
l.logger.Debug().
Interface("invitation", "list").
Msg("List")

return l.next.List(ctx, userId)
}

func (l logging) GetByInvitedEmail(ctx context.Context, email string) (*invitations.Invitation, error) {
l.logger.Debug().
Interface("invitation", email).
Msg("Get")

return l.next.GetByInvitedEmail(ctx, email)
}

// Invite implements the Service interface.
func (l logging) Invite(ctx context.Context, invitation *invitations.Invitation) (*invitations.Invitation, error) {
l.logger.Debug().
Expand Down
9 changes: 9 additions & 0 deletions services/invitations/pkg/service/v0/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service
import (
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/services/invitations/pkg/config"
"go-micro.dev/v4/store"
)

// Option defines a single option function.
Expand All @@ -12,6 +13,8 @@ type Option func(o *Options)
type Options struct {
Logger log.Logger
Config *config.Config

Persistance *store.Store
}

// newOptions initializes the available default options.
Expand All @@ -38,3 +41,9 @@ func Config(val *config.Config) Option {
o.Config = val
}
}

func WithPersistance(val *store.Store) Option {
return func(o *Options) {
o.Persistance = val
}
}
Loading