Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
@@ -0,0 +1,53 @@
package config
import (
"context"
"github.com/qsfera/server/pkg/shared"
)
// Config combines all available configuration parts.
type Config struct {
Commons *shared.Commons `yaml:"-"` // don't use this directly as configuration for a service
Service Service `yaml:"-"`
LogLevel string `yaml:"loglevel" env:"OC_LOG_LEVEL;WEBFINGER_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." introductionVersion:"1.0.0"`
Debug Debug `yaml:"debug"`
HTTP HTTP `yaml:"http"`
Instances []Instance `yaml:"instances"`
Relations []string `yaml:"relations" env:"WEBFINGER_RELATIONS" desc:"A list of relation URIs or registered relation types to add to webfinger responses. See the Environment Variable Types description for more details." introductionVersion:"1.0.0"`
IDP string `yaml:"idp" env:"OC_URL;OC_OIDC_ISSUER;WEBFINGER_OIDC_ISSUER" desc:"The identity provider href for the openid-discovery relation." introductionVersion:"1.0.0"`
AndroidClientID string `yaml:"android_client_id" env:"OC_OIDC_CLIENT_ID;WEBFINGER_ANDROID_OIDC_CLIENT_ID" desc:"The OIDC client ID for Android app." introductionVersion:"6.0.0"`
AndroidClientScopes []string `yaml:"android_client_scopes" env:"OC_OIDC_CLIENT_SCOPES;WEBFINGER_ANDROID_OIDC_CLIENT_SCOPES" desc:"The OIDC client scopes the Android app should request." introductionVersion:"6.0.0"`
DesktopClientID string `yaml:"desktop_client_id" env:"OC_OIDC_CLIENT_ID;WEBFINGER_DESKTOP_OIDC_CLIENT_ID" desc:"The OIDC client ID for the КуСфера desktop application." introductionVersion:"6.0.0"`
DesktopClientScopes []string `yaml:"desktop_client_scopes" env:"OC_OIDC_CLIENT_SCOPES;WEBFINGER_DESKTOP_OIDC_CLIENT_SCOPES" desc:"The OIDC client scopes the КуСфера desktop application should request." introductionVersion:"6.0.0"`
IOSClientID string `yaml:"ios_client_id" env:"OC_OIDC_CLIENT_ID;WEBFINGER_IOS_OIDC_CLIENT_ID" desc:"The OIDC client ID for the IOS app." introductionVersion:"6.0.0"`
IOSClientScopes []string `yaml:"ios_client_scopes" env:"OC_OIDC_CLIENT_SCOPES;WEBFINGER_IOS_OIDC_CLIENT_SCOPES" desc:"The OIDC client scopes the IOS app should request." introductionVersion:"6.0.0"`
// The WEB_OIDC_CLIENT_ID is kept for backware compatibility with the old settings from the `web` service and can be removed in a future release.
WebClientID string `yaml:"web_client_id" env:"OC_OIDC_CLIENT_ID;WEB_OIDC_CLIENT_ID;WEBFINGER_WEB_OIDC_CLIENT_ID" desc:"The OIDC client ID for the КуСфера web client. The 'WEB_OIDC_CLIENT_ID' setting is only here for backwards compatibility and will be remove in a future release." introductionVersion:"6.0.0"`
// The WEB_OIDC_SCOPE is kept for backware compatibility with the old settings from the `web` service and can be removed in a future release.
WebClientScopes []string `yaml:"web_client_scopes" env:"OC_OIDC_CLIENT_SCOPES;WEB_OIDC_SCOPE;WEBFINGER_WEB_OIDC_CLIENT_SCOPES" desc:"The OIDC client scopes the КуСфера web client should request. The 'WEB_OIDC_SCOPE' setting is only here for backwards compatibility and will be remove in a future release." introductionVersion:"6.0.0"`
QsferaURL string `yaml:"qsfera_url" env:"OC_URL;WEBFINGER_QSFERA_SERVER_INSTANCE_URL" desc:"The URL for the legacy server instance relation. It defaults to the OC_URL but can be overridden to support some reverse proxy corner cases. To shard the deployment, multiple instances can be configured in the configuration file." introductionVersion:"1.0.0"`
Insecure bool `yaml:"insecure" env:"OC_INSECURE;WEBFINGER_INSECURE" desc:"Allow insecure connections to the WEBFINGER service." introductionVersion:"1.0.0"`
OIDCClientConfigs map[string]OIDCClientConfig `yaml:"-"`
Context context.Context `yaml:"-"`
}
// Instance to use with a matching rule and titles
type Instance struct {
Claim string `yaml:"claim"`
Regex string `yaml:"regex"`
Href string `yaml:"href"`
Titles map[string]string `yaml:"titles"`
Break bool `yaml:"break"`
}
type OIDCClientConfig struct {
ClientID string
Scopes []string
}
@@ -0,0 +1,9 @@
package config
// Debug defines the available debug configuration.
type Debug struct {
Addr string `yaml:"addr" env:"WEBFINGER_DEBUG_ADDR" desc:"Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed." introductionVersion:"1.0.0"`
Token string `yaml:"token" env:"WEBFINGER_DEBUG_TOKEN" desc:"Token to secure the metrics endpoint." introductionVersion:"1.0.0"`
Pprof bool `yaml:"pprof" env:"WEBFINGER_DEBUG_PPROF" desc:"Enables pprof, which can be used for profiling." introductionVersion:"1.0.0"`
Zpages bool `yaml:"zpages" env:"WEBFINGER_DEBUG_ZPAGES" desc:"Enables zpages, which can be used for collecting and viewing in-memory traces." introductionVersion:"1.0.0"`
}
@@ -0,0 +1,113 @@
package defaults
import (
"strings"
"github.com/qsfera/server/services/webfinger/pkg/config"
"github.com/qsfera/server/services/webfinger/pkg/relations"
)
var (
nativeAppScopes = []string{"openid", "profile", "email", "offline_access"}
webAppScopes = []string{"openid", "profile", "email"}
)
// FullDefaultConfig returns a fully initialized default configuration
func FullDefaultConfig() *config.Config {
cfg := DefaultConfig()
EnsureDefaults(cfg)
Sanitize(cfg)
return cfg
}
// DefaultConfig returns a basic default configuration
func DefaultConfig() *config.Config {
return &config.Config{
Debug: config.Debug{
Addr: "127.0.0.1:9279",
Token: "",
Pprof: false,
Zpages: false,
},
HTTP: config.HTTP{
Addr: "127.0.0.1:9275",
Root: "/",
Namespace: "qsfera.web",
CORS: config.CORS{
AllowedOrigins: []string{"https://localhost:9200"},
AllowCredentials: false,
},
},
Service: config.Service{
Name: "webfinger",
},
QsferaURL: "https://localhost:9200",
Relations: []string{relations.OpenIDConnectRel, relations.QsferaInstanceRel},
Instances: []config.Instance{
{
Claim: "sub",
Regex: ".+",
Href: "{{.OC_URL}}",
Titles: map[string]string{
"en": "КуСфера Instance",
},
},
},
IDP: "https://localhost:9200",
Insecure: false,
AndroidClientID: "QsferaAndroid",
AndroidClientScopes: nativeAppScopes,
DesktopClientID: "QsferaDesktop",
DesktopClientScopes: nativeAppScopes,
IOSClientID: "QsferaIOS",
IOSClientScopes: nativeAppScopes,
WebClientID: "web",
WebClientScopes: webAppScopes,
}
}
// EnsureDefaults adds default values to the configuration if they are not set yet
func EnsureDefaults(cfg *config.Config) {
if cfg.LogLevel == "" {
cfg.LogLevel = "error"
}
if cfg.Commons != nil {
cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
}
if (cfg.Commons != nil && cfg.Commons.QsferaURL != "") &&
(cfg.HTTP.CORS.AllowedOrigins == nil ||
len(cfg.HTTP.CORS.AllowedOrigins) == 1 &&
cfg.HTTP.CORS.AllowedOrigins[0] == "https://localhost:9200") {
cfg.HTTP.CORS.AllowedOrigins = []string{cfg.Commons.QsferaURL}
}
}
// Sanitize sanitized the configuration
func Sanitize(cfg *config.Config) {
// sanitize config
if cfg.HTTP.Root != "/" {
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
}
cfg.OIDCClientConfigs = map[string]config.OIDCClientConfig{
"android": {
ClientID: cfg.AndroidClientID,
Scopes: cfg.AndroidClientScopes,
},
"desktop": {
ClientID: cfg.DesktopClientID,
Scopes: cfg.DesktopClientScopes,
},
"ios": {
ClientID: cfg.IOSClientID,
Scopes: cfg.IOSClientScopes,
},
"web": {
ClientID: cfg.WebClientID,
Scopes: cfg.WebClientScopes,
},
}
}
@@ -0,0 +1,20 @@
package config
import "github.com/qsfera/server/pkg/shared"
// CORS defines the available cors configuration.
type CORS struct {
AllowedOrigins []string `yaml:"allow_origins" env:"OC_CORS_ALLOW_ORIGINS;WEBFINGER_CORS_ALLOW_ORIGINS" desc:"A list of allowed CORS origins. See following chapter for more details: *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin. See the Environment Variable Types description for more details." introductionVersion:"1.0.0"`
AllowedMethods []string `yaml:"allow_methods" env:"OC_CORS_ALLOW_METHODS;WEBFINGER_CORS_ALLOW_METHODS" desc:"A list of allowed CORS methods. See following chapter for more details: *Access-Control-Request-Method* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Method. See the Environment Variable Types description for more details." introductionVersion:"1.0.0"`
AllowedHeaders []string `yaml:"allow_headers" env:"OC_CORS_ALLOW_HEADERS;WEBFINGER_CORS_ALLOW_HEADERS" desc:"A list of allowed CORS headers. See following chapter for more details: *Access-Control-Request-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers. See the Environment Variable Types description for more details." introductionVersion:"1.0.0"`
AllowCredentials bool `yaml:"allow_credentials" env:"OC_CORS_ALLOW_CREDENTIALS;WEBFINGER_CORS_ALLOW_CREDENTIALS" desc:"Allow credentials for CORS.See following chapter for more details: *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials." introductionVersion:"1.0.0"`
}
// HTTP defines the available http configuration.
type HTTP struct {
Addr string `yaml:"addr" env:"WEBFINGER_HTTP_ADDR" desc:"The bind address of the HTTP service." introductionVersion:"1.0.0"`
Namespace string `yaml:"-"`
Root string `yaml:"root" env:"WEBFINGER_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service." introductionVersion:"1.0.0"`
CORS CORS `yaml:"cors"`
TLS shared.HTTPServiceTLS `yaml:"tls"`
}
@@ -0,0 +1,37 @@
package parser
import (
"errors"
occfg "github.com/qsfera/server/pkg/config"
"github.com/qsfera/server/services/webfinger/pkg/config"
"github.com/qsfera/server/services/webfinger/pkg/config/defaults"
"github.com/qsfera/server/pkg/config/envdecode"
)
// ParseConfig loads configuration from known paths.
func ParseConfig(cfg *config.Config) error {
err := occfg.BindSourcesToStructs(cfg.Service.Name, cfg)
if err != nil {
return err
}
defaults.EnsureDefaults(cfg)
// load all env variables relevant to the config in the current context.
if err := envdecode.Decode(cfg); err != nil {
// no environment variable set for this config is an expected "error"
if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) {
return err
}
}
defaults.Sanitize(cfg)
return Validate(cfg)
}
func Validate(_ *config.Config) error {
return nil
}
@@ -0,0 +1,6 @@
package config
// Service defines the available service configuration.
type Service struct {
Name string `yaml:"-"`
}