feat: initial commit

This commit is contained in:
2025-02-21 18:42:56 +01:00
commit ee4a65b345
81 changed files with 3441 additions and 0 deletions

View File

@ -0,0 +1,138 @@
package setup
import (
"context"
"crypto/rand"
"fmt"
"forge.cadoles.com/wpetit/clearcase/internal/config"
"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/auth"
"github.com/gorilla/sessions"
"github.com/markbates/goth"
"github.com/markbates/goth/gothic"
"github.com/markbates/goth/providers/github"
"github.com/markbates/goth/providers/google"
"github.com/markbates/goth/providers/openidConnect"
"github.com/pkg/errors"
)
func NewAuthHandlerFromConfig(ctx context.Context, conf *config.Config) (*auth.Handler, error) {
// Configure sessions store
keyPairs := make([][]byte, 0)
if len(conf.HTTP.Session.Keys) == 0 {
key, err := getRandomBytes(32)
if err != nil {
return nil, errors.Wrap(err, "could not generate cookie signing key")
}
keyPairs = append(keyPairs, key)
} else {
for _, k := range conf.HTTP.Session.Keys {
keyPairs = append(keyPairs, []byte(k))
}
}
sessionStore := sessions.NewCookieStore(keyPairs...)
sessionStore.MaxAge(int(conf.HTTP.Session.Cookie.MaxAge))
sessionStore.Options.Path = conf.HTTP.Session.Cookie.Path
sessionStore.Options.HttpOnly = conf.HTTP.Session.Cookie.HTTPOnly
sessionStore.Options.Secure = conf.HTTP.Session.Cookie.Secure
// Configure providers
gothProviders := make([]goth.Provider, 0)
providers := make([]auth.Provider, 0)
if conf.Auth.Providers.Google.Key != "" && conf.Auth.Providers.Google.Secret != "" {
googleProvider := google.New(
conf.Auth.Providers.Google.Key,
conf.Auth.Providers.Google.Secret,
fmt.Sprintf("%s/auth/providers/google/callback", conf.HTTP.BaseURL),
conf.Auth.Providers.Google.Scopes...,
)
gothProviders = append(gothProviders, googleProvider)
providers = append(providers, auth.Provider{
ID: googleProvider.Name(),
Label: "Google",
Icon: "fa-google",
})
}
if conf.Auth.Providers.Github.Key != "" && conf.Auth.Providers.Github.Secret != "" {
githubProvider := github.New(
conf.Auth.Providers.Github.Key,
conf.Auth.Providers.Github.Secret,
fmt.Sprintf("%s/auth/providers/github/callback", conf.HTTP.BaseURL),
conf.Auth.Providers.Github.Scopes...,
)
gothProviders = append(gothProviders, githubProvider)
providers = append(providers, auth.Provider{
ID: githubProvider.Name(),
Label: "Github",
Icon: "fa-github",
})
}
if conf.Auth.Providers.OIDC.Key != "" && conf.Auth.Providers.OIDC.Secret != "" {
oidcProvider, err := openidConnect.New(
conf.Auth.Providers.OIDC.Key,
conf.Auth.Providers.OIDC.Secret,
fmt.Sprintf("%s/auth/providers/openid-connect/callback", conf.HTTP.BaseURL),
conf.Auth.Providers.OIDC.DiscoveryURL,
conf.Auth.Providers.OIDC.Scopes...,
)
if err != nil {
return nil, errors.Wrap(err, "could not configure oidc provider")
}
gothProviders = append(gothProviders, oidcProvider)
providers = append(providers, auth.Provider{
ID: oidcProvider.Name(),
Label: conf.Auth.Providers.OIDC.Label,
Icon: conf.Auth.Providers.OIDC.Icon,
})
}
goth.UseProviders(gothProviders...)
gothic.Store = sessionStore
opts := []auth.OptionFunc{
auth.WithProviders(providers...),
}
if conf.Auth.DefaultAdmin.Email != "" && conf.Auth.DefaultAdmin.Provider != "" {
opts = append(opts, auth.WithDefaultAdmin(auth.DefaultAdmin{
Provider: conf.Auth.DefaultAdmin.Provider,
Email: conf.Auth.DefaultAdmin.Email,
}))
}
auth := auth.NewHandler(
sessionStore,
opts...,
)
return auth, nil
}
func getRandomBytes(n int) ([]byte, error) {
data := make([]byte, n)
read, err := rand.Read(data)
if err != nil {
return nil, errors.WithStack(err)
}
if read != n {
return nil, errors.Errorf("could not read %d bytes", n)
}
return data, nil
}

View File

@ -0,0 +1,27 @@
package setup
import (
"context"
"forge.cadoles.com/wpetit/clearcase/internal/config"
"forge.cadoles.com/wpetit/clearcase/internal/http"
"github.com/pkg/errors"
)
func NewHTTPServerFromConfig(ctx context.Context, conf *config.Config) (*http.Server, error) {
// Configure Web UI handler
webui, err := NewWebUIHandlerFromConfig(ctx, conf)
if err != nil {
return nil, errors.Wrap(err, "could not configure webui handler from config")
}
// Create HTTP server
server := http.NewServer(
webui,
http.WithAddress(conf.HTTP.Address),
http.WithBaseURL(conf.HTTP.BaseURL),
)
return server, nil
}

View File

@ -0,0 +1,12 @@
package setup
import (
"context"
"forge.cadoles.com/wpetit/clearcase/internal/config"
"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/issue"
)
func NewIssueHandlerFromConfig(ctx context.Context, conf *config.Config) (*issue.Handler, error) {
return issue.NewHandler(), nil
}

View File

@ -0,0 +1,44 @@
package setup
import (
"net/url"
"github.com/pkg/errors"
)
var ErrNotRegistered = errors.New("not registered")
type Factory[T any] func(u *url.URL) (T, error)
type Registry[T any] struct {
mappings map[string]Factory[T]
}
func (r *Registry[T]) Register(scheme string, factory Factory[T]) {
r.mappings[scheme] = factory
}
func (r *Registry[T]) From(rawURL string) (T, error) {
u, err := url.Parse(rawURL)
if err != nil {
return *new(T), errors.WithStack(err)
}
factory, exists := r.mappings[u.Scheme]
if !exists {
return *new(T), errors.Wrapf(ErrNotRegistered, "scheme '%s' not found", u.Scheme)
}
value, err := factory(u)
if err != nil {
return *new(T), errors.WithStack(err)
}
return value, nil
}
func NewRegistry[T any]() *Registry[T] {
return &Registry[T]{
mappings: make(map[string]Factory[T]),
}
}

View File

@ -0,0 +1,48 @@
package setup
import (
"context"
"forge.cadoles.com/wpetit/clearcase/internal/config"
"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui"
"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/common"
"github.com/pkg/errors"
)
func NewWebUIHandlerFromConfig(ctx context.Context, conf *config.Config) (*webui.Handler, error) {
opts := make([]webui.OptionFunc, 0)
// Configure auth handler
authHandler, err := NewAuthHandlerFromConfig(ctx, conf)
if err != nil {
return nil, errors.Wrap(err, "could not configure auth handler from config")
}
authMiddleware := authHandler.Middleware()
opts = append(opts, webui.WithMount("/auth/", authHandler))
// Configure issue handler
issueHandler, err := NewIssueHandlerFromConfig(ctx, conf)
if err != nil {
return nil, errors.Wrap(err, "could not configure explorer handler from config")
}
opts = append(opts, webui.WithMount("/", authMiddleware(issueHandler)))
// Configure common handler
commonHandler, err := NewCommonHandlerFromConfig(ctx, conf)
if err != nil {
return nil, errors.Wrap(err, "could not configure common handler from config")
}
opts = append(opts, webui.WithMount("/assets/", commonHandler))
handler := webui.NewHandler(opts...)
return handler, nil
}
func NewCommonHandlerFromConfig(ctx context.Context, conf *config.Config) (*common.Handler, error) {
return common.NewHandler(), nil
}