mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
Config: Refactor OIDC options and report #782
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
8c67fb1fe8
commit
a436dc3fd8
12 changed files with 121 additions and 22 deletions
|
|
@ -59,7 +59,6 @@ services:
|
|||
PHOTOPRISM_OIDC_ISSUER: "https://keycloak.localssl.dev/auth/realms/master"
|
||||
PHOTOPRISM_OIDC_CLIENT: "photoprism-develop"
|
||||
PHOTOPRISM_OIDC_SECRET: "9d8351a0-ca01-4556-9c37-85eb634869b9"
|
||||
PHOTOPRISM_OIDC_SCOPES: "openid profile"
|
||||
PHOTOPRISM_OIDC_INSECURE: "true"
|
||||
## Site Information
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/" # server URL in the format "http(s)://domain.name(:port)/(path)"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ var ConfigReports = []Report{
|
|||
{Title: "Global Config Options", NoWrap: true, Report: func(conf *config.Config) ([][]string, []string) {
|
||||
return conf.Report()
|
||||
}},
|
||||
{Title: "OpenID Connect", NoWrap: true, Report: func(conf *config.Config) ([][]string, []string) {
|
||||
return conf.OIDCReport()
|
||||
}},
|
||||
}
|
||||
|
||||
// showConfigAction shows global config option names and values.
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func (f CliFlags) Replace(name string, replacement CliFlag) CliFlags {
|
|||
return f
|
||||
}
|
||||
|
||||
// Insert inserts command flags, if possible after name.
|
||||
// Insert inserts command flags, if possible after the flag specified by name.
|
||||
func (f CliFlags) Insert(name string, insert []CliFlag) (result CliFlags) {
|
||||
result = make(CliFlags, 0, len(f)+len(insert))
|
||||
|
||||
|
|
@ -92,6 +92,29 @@ func (f CliFlags) Insert(name string, insert []CliFlag) (result CliFlags) {
|
|||
return result
|
||||
}
|
||||
|
||||
// InsertBefore inserts command flags, if possible before the flag specified by name.
|
||||
func (f CliFlags) InsertBefore(name string, insert []CliFlag) (result CliFlags) {
|
||||
result = make(CliFlags, 0, len(f)+len(insert))
|
||||
|
||||
done := false
|
||||
|
||||
for _, flag := range f {
|
||||
if !done && flag.Name() == name {
|
||||
result = append(result, insert...)
|
||||
done = true
|
||||
}
|
||||
|
||||
result = append(result, flag)
|
||||
}
|
||||
|
||||
if !done {
|
||||
log.Warnf("config: failed to insert cli flags before %s", clean.Log(name))
|
||||
result = append(result, insert...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Prepend adds command flags at the beginning.
|
||||
func (f CliFlags) Prepend(el []CliFlag) (result CliFlags) {
|
||||
result = make(CliFlags, 0, len(f)+len(el))
|
||||
|
|
|
|||
|
|
@ -1,17 +1,36 @@
|
|||
package config
|
||||
|
||||
const OIDCDefaultScopes = "openid profile"
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const OIDCDefaultScopes = "openid email profile"
|
||||
|
||||
// OIDCEnabled checks if login via OpenID Connect (OIDC) is enabled.
|
||||
func (c *Config) OIDCEnabled() bool {
|
||||
return c.options.OIDCIssuer != "" && c.options.OIDCClient != "" && c.options.OIDCSecret != ""
|
||||
}
|
||||
|
||||
// OIDCIssuer returns the OpenID Connect Issuer URL for single sign-on via OIDC.
|
||||
// OIDCIssuer returns the OpenID Connect Issuer URL as string for single sign-on via OIDC.
|
||||
func (c *Config) OIDCIssuer() string {
|
||||
return c.options.OIDCIssuer
|
||||
}
|
||||
|
||||
// OIDCIssuerURL returns the OpenID Connect Issuer URL as *url.URL for single sign-on via OIDC.
|
||||
func (c *Config) OIDCIssuerURL() *url.URL {
|
||||
if oidcIssuer := c.OIDCIssuer(); oidcIssuer == "" {
|
||||
return &url.URL{}
|
||||
} else if result, err := url.Parse(oidcIssuer); err != nil {
|
||||
log.Errorf("oidc: failed to parse issuer URL (%s)", err)
|
||||
return &url.URL{}
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// OIDCClient returns the Client ID for single sign-on via OIDC.
|
||||
func (c *Config) OIDCClient() string {
|
||||
return c.options.OIDCClient
|
||||
|
|
@ -22,17 +41,37 @@ func (c *Config) OIDCSecret() string {
|
|||
return c.options.OIDCSecret
|
||||
}
|
||||
|
||||
// OIDCScopes returns the token request scopes for single sign-on via OIDC.
|
||||
// OIDCScopes returns the user information scopes for single sign-on via OIDC.
|
||||
func (c *Config) OIDCScopes() string {
|
||||
if c.options.OIDCScopes == "" {
|
||||
return OIDCDefaultScopes
|
||||
}
|
||||
|
||||
return c.options.OIDCScopes
|
||||
}
|
||||
|
||||
// OIDCInsecure checks if OIDC issuer SSL/TLS certificate verification should be skipped.
|
||||
func (c *Config) OIDCInsecure() bool {
|
||||
return c.options.OIDCInsecure
|
||||
}
|
||||
|
||||
// OIDCRegister checks if new accounts may be created via OIDC.
|
||||
func (c *Config) OIDCRegister() bool {
|
||||
return c.options.OIDCRegister
|
||||
}
|
||||
|
||||
// OIDCInsecure checks if OIDC issuer SSL/TLS certificate verification should be skipped.
|
||||
func (c *Config) OIDCInsecure() bool {
|
||||
return c.options.OIDCInsecure
|
||||
// OIDCReport returns the OpenID Connect config values as a table for reporting.
|
||||
func (c *Config) OIDCReport() (rows [][]string, cols []string) {
|
||||
cols = []string{"Name", "Value"}
|
||||
|
||||
rows = [][]string{
|
||||
{"oidc-issuer", c.OIDCIssuer()},
|
||||
{"oidc-client", c.OIDCClient()},
|
||||
{"oidc-secret", strings.Repeat("*", utf8.RuneCountInString(c.OIDCSecret()))},
|
||||
{"oidc-scopes", c.OIDCScopes()},
|
||||
{"oidc-insecure", fmt.Sprintf("%t", c.OIDCInsecure())},
|
||||
{"oidc-register", fmt.Sprintf("%t", c.OIDCRegister())},
|
||||
}
|
||||
|
||||
return rows, cols
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -18,6 +19,12 @@ func TestConfig_OIDCIssuer(t *testing.T) {
|
|||
assert.Equal(t, "", c.OIDCIssuer())
|
||||
}
|
||||
|
||||
func TestConfig_OIDCIssuerURL(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.IsType(t, &url.URL{}, c.OIDCIssuerURL())
|
||||
}
|
||||
|
||||
func TestConfig_OIDCClient(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
|
|
@ -33,7 +40,7 @@ func TestConfig_OIDCSecret(t *testing.T) {
|
|||
func TestConfig_OIDCScopes(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, "openid profile", c.OIDCScopes())
|
||||
assert.Equal(t, OIDCDefaultScopes, c.OIDCScopes())
|
||||
}
|
||||
|
||||
func TestConfig_OIDCRegister(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -63,20 +63,21 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: cli.StringFlag{
|
||||
Name: "oidc-scopes",
|
||||
Hidden: true,
|
||||
Usage: "user information `SCOPES` for single sign-on via OIDC",
|
||||
Value: OIDCDefaultScopes,
|
||||
EnvVar: EnvVar("OIDC_SCOPES"),
|
||||
}}, {
|
||||
Flag: cli.BoolFlag{
|
||||
Name: "oidc-register",
|
||||
Usage: "allow creating new accounts via OIDC",
|
||||
EnvVar: EnvVar("OIDC_REGISTER"),
|
||||
}}, {
|
||||
Flag: cli.BoolFlag{
|
||||
Name: "oidc-insecure",
|
||||
Usage: "skip issuer SSL/TLS certificate verification",
|
||||
EnvVar: EnvVar("OIDC_INSECURE"),
|
||||
}}, {
|
||||
Flag: cli.BoolFlag{
|
||||
Name: "oidc-register",
|
||||
Usage: "allow user registration via OIDC",
|
||||
EnvVar: EnvVar("OIDC_REGISTER"),
|
||||
}}, {
|
||||
Flag: cli.Int64Flag{
|
||||
Name: "session-maxage",
|
||||
Value: DefaultSessionMaxAge,
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ type Options struct {
|
|||
OIDCClient string `yaml:"OIDCClient" json:"-" flag:"oidc-client"`
|
||||
OIDCSecret string `yaml:"OIDCSecret" json:"-" flag:"oidc-secret"`
|
||||
OIDCScopes string `yaml:"OIDCScopes" json:"-" flag:"oidc-scopes"`
|
||||
OIDCRegister bool `yaml:"OIDCRegister" json:"-" flag:"oidc-register"`
|
||||
OIDCInsecure bool `yaml:"OIDCInsecure" json:"-" flag:"oidc-insecure"`
|
||||
OIDCRegister bool `yaml:"OIDCRegister" json:"-" flag:"oidc-register"`
|
||||
SessionMaxAge int64 `yaml:"SessionMaxAge" json:"-" flag:"session-maxage"`
|
||||
SessionTimeout int64 `yaml:"SessionTimeout" json:"-" flag:"session-timeout"`
|
||||
SessionCache int64 `yaml:"SessionCache" json:"-" flag:"session-cache"`
|
||||
|
|
|
|||
|
|
@ -28,12 +28,6 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
|||
{"password-reset-uri", c.PasswordResetUri()},
|
||||
{"register-uri", c.RegisterUri()},
|
||||
{"login-uri", c.LoginUri()},
|
||||
{"oidc-issuer", c.OIDCIssuer()},
|
||||
{"oidc-client", c.OIDCClient()},
|
||||
{"oidc-secret", c.OIDCSecret()},
|
||||
{"oidc-scopes", c.OIDCScopes()},
|
||||
{"oidc-register", fmt.Sprintf("%t", c.OIDCRegister())},
|
||||
{"oidc-insecure", fmt.Sprintf("%t", c.OIDCInsecure())},
|
||||
{"session-maxage", fmt.Sprintf("%d", c.SessionMaxAge())},
|
||||
{"session-timeout", fmt.Sprintf("%d", c.SessionTimeout())},
|
||||
{"session-cache", fmt.Sprintf("%d", c.SessionCache())},
|
||||
|
|
|
|||
26
internal/get/oidc.go
Normal file
26
internal/get/oidc.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package get
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/oidc"
|
||||
)
|
||||
|
||||
var onceOidc sync.Once
|
||||
|
||||
func initOidc() {
|
||||
services.OIDC, _ = oidc.NewClient(
|
||||
Config().OIDCIssuerURL(),
|
||||
Config().OIDCClient(),
|
||||
Config().OIDCSecret(),
|
||||
Config().OIDCScopes(),
|
||||
Config().SiteUrl(),
|
||||
Config().Debug(),
|
||||
)
|
||||
}
|
||||
|
||||
func OIDC() *oidc.Client {
|
||||
oncePhotos.Do(initOidc)
|
||||
|
||||
return services.OIDC
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/face"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/oidc"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/session"
|
||||
|
|
@ -58,6 +59,7 @@ var services struct {
|
|||
Query *query.Query
|
||||
Thumbs *photoprism.Thumbs
|
||||
Session *session.Session
|
||||
OIDC *oidc.Client
|
||||
}
|
||||
|
||||
func SetConfig(c *config.Config) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/classify"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/oidc"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/session"
|
||||
|
|
@ -72,3 +73,7 @@ func TestResample(t *testing.T) {
|
|||
func TestSession(t *testing.T) {
|
||||
assert.IsType(t, &session.Session{}, Session())
|
||||
}
|
||||
|
||||
func TestOIDC(t *testing.T) {
|
||||
assert.IsType(t, &oidc.Client{}, OIDC())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ func NewClient(iss *url.URL, clientId, clientSecret, customScopes, siteUrl strin
|
|||
}
|
||||
}
|
||||
|
||||
scopes := strings.Split(strings.TrimSpace("openid profile email "+customScopes), " ")
|
||||
scopes := strings.Split(strings.TrimSpace("openid email profile "+customScopes), " ")
|
||||
|
||||
provider, err := rp.NewRelyingPartyOIDC(iss.String(), clientId, clientSecret, u.String(), scopes, clientOpt...)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue