mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
OIDC: Add additional config options and OAuth2 API endpoints #782
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
10
compose.yaml
10
compose.yaml
@@ -45,6 +45,11 @@ services:
|
|||||||
PHOTOPRISM_AUTH_MODE: "password" # authentication mode (public, password)
|
PHOTOPRISM_AUTH_MODE: "password" # authentication mode (public, password)
|
||||||
PHOTOPRISM_REGISTER_URI: "https://keycloak.localssl.dev/admin/"
|
PHOTOPRISM_REGISTER_URI: "https://keycloak.localssl.dev/admin/"
|
||||||
PHOTOPRISM_PASSWORD_RESET_URI: "https://keycloak.localssl.dev/realms/master/login-actions/reset-credentials"
|
PHOTOPRISM_PASSWORD_RESET_URI: "https://keycloak.localssl.dev/realms/master/login-actions/reset-credentials"
|
||||||
|
## OpenID Connect (pre-configured for local tests):
|
||||||
|
PHOTOPRISM_OIDC_URI: "https://keycloak.localssl.dev/auth/realms/master"
|
||||||
|
PHOTOPRISM_OIDC_INSECURE: "true"
|
||||||
|
PHOTOPRISM_OIDC_CLIENT: "photoprism-develop"
|
||||||
|
PHOTOPRISM_OIDC_SECRET: "9d8351a0-ca01-4556-9c37-85eb634869b9"
|
||||||
## LDAP Authentication (pre-configured for local tests):
|
## LDAP Authentication (pre-configured for local tests):
|
||||||
PHOTOPRISM_LDAP_URI: "ldap://dummy-ldap:389"
|
PHOTOPRISM_LDAP_URI: "ldap://dummy-ldap:389"
|
||||||
PHOTOPRISM_LDAP_INSECURE: "true"
|
PHOTOPRISM_LDAP_INSECURE: "true"
|
||||||
@@ -55,11 +60,6 @@ services:
|
|||||||
PHOTOPRISM_LDAP_ROLE: ""
|
PHOTOPRISM_LDAP_ROLE: ""
|
||||||
PHOTOPRISM_LDAP_ROLE_DN: "ou=photoprism-*,ou=groups,dc=localssl,dc=dev"
|
PHOTOPRISM_LDAP_ROLE_DN: "ou=photoprism-*,ou=groups,dc=localssl,dc=dev"
|
||||||
PHOTOPRISM_LDAP_WEBDAV_DN: "ou=photoprism-webdav,ou=groups,dc=localssl,dc=dev"
|
PHOTOPRISM_LDAP_WEBDAV_DN: "ou=photoprism-webdav,ou=groups,dc=localssl,dc=dev"
|
||||||
## OpenID Connect (pre-configured for local tests):
|
|
||||||
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_INSECURE: "true"
|
|
||||||
## Site Information
|
## Site Information
|
||||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/" # server URL in the format "http(s)://domain.name(:port)/(path)"
|
PHOTOPRISM_SITE_URL: "http://localhost:2342/" # server URL in the format "http(s)://domain.name(:port)/(path)"
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ func TestMain(m *testing.M) {
|
|||||||
// Init test config.
|
// Init test config.
|
||||||
c := config.TestConfig()
|
c := config.TestConfig()
|
||||||
get.SetConfig(c)
|
get.SetConfig(c)
|
||||||
|
defer c.CloseDb()
|
||||||
|
|
||||||
// Increase login rate limit for testing.
|
// Increase login rate limit for testing.
|
||||||
limiter.Login = limiter.NewLimit(1, 10000)
|
limiter.Login = limiter.NewLimit(1, 10000)
|
||||||
@@ -48,9 +49,6 @@ func TestMain(m *testing.M) {
|
|||||||
// Run unit tests.
|
// Run unit tests.
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
// Close database connection.
|
|
||||||
_ = c.CloseDb()
|
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
internal/api/oauth_authorize.go
Normal file
46
internal/api/oauth_authorize.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/internal/event"
|
||||||
|
"github.com/photoprism/photoprism/internal/get"
|
||||||
|
"github.com/photoprism/photoprism/pkg/authn"
|
||||||
|
"github.com/photoprism/photoprism/pkg/header"
|
||||||
|
"github.com/photoprism/photoprism/pkg/i18n"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OAuthAuthorize is a starting point for browser-based OpenID Connect flows.
|
||||||
|
//
|
||||||
|
// GET /api/v1/oauth/authorize
|
||||||
|
func OAuthAuthorize(router *gin.RouterGroup) {
|
||||||
|
router.GET("/oauth/authorize", func(c *gin.Context) {
|
||||||
|
// Prevent CDNs from caching this endpoint.
|
||||||
|
if header.IsCdn(c.Request) {
|
||||||
|
AbortNotFound(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable caching of responses.
|
||||||
|
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||||
|
|
||||||
|
// Get client IP address for logs and rate limiting checks.
|
||||||
|
clientIp := ClientIP(c)
|
||||||
|
actor := "unknown client"
|
||||||
|
action := "authorize"
|
||||||
|
|
||||||
|
// Abort if running in public mode.
|
||||||
|
if get.Config().Public() {
|
||||||
|
event.AuditErr([]string{clientIp, "oauth2", actor, action, authn.ErrDisabledInPublicMode.Error()})
|
||||||
|
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Send response.
|
||||||
|
c.JSON(http.StatusOK, gin.H{"status": StatusSuccess})
|
||||||
|
})
|
||||||
|
}
|
||||||
46
internal/api/oauth_logout.go
Normal file
46
internal/api/oauth_logout.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/internal/event"
|
||||||
|
"github.com/photoprism/photoprism/internal/get"
|
||||||
|
"github.com/photoprism/photoprism/pkg/authn"
|
||||||
|
"github.com/photoprism/photoprism/pkg/header"
|
||||||
|
"github.com/photoprism/photoprism/pkg/i18n"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OAuthLogout implements a single logout for OIDC-authenticated clients.
|
||||||
|
//
|
||||||
|
// GET /api/v1/oauth/logout
|
||||||
|
func OAuthLogout(router *gin.RouterGroup) {
|
||||||
|
router.GET("/oauth/logout", func(c *gin.Context) {
|
||||||
|
// Prevent CDNs from caching this endpoint.
|
||||||
|
if header.IsCdn(c.Request) {
|
||||||
|
AbortNotFound(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable caching of responses.
|
||||||
|
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||||
|
|
||||||
|
// Get client IP address for logs and rate limiting checks.
|
||||||
|
clientIp := ClientIP(c)
|
||||||
|
actor := "unknown client"
|
||||||
|
action := "logout"
|
||||||
|
|
||||||
|
// Abort if running in public mode.
|
||||||
|
if get.Config().Public() {
|
||||||
|
event.AuditErr([]string{clientIp, "oauth2", actor, action, authn.ErrDisabledInPublicMode.Error()})
|
||||||
|
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Send response.
|
||||||
|
c.JSON(http.StatusOK, gin.H{"status": StatusSuccess})
|
||||||
|
})
|
||||||
|
}
|
||||||
46
internal/api/oauth_redirect.go
Normal file
46
internal/api/oauth_redirect.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/internal/event"
|
||||||
|
"github.com/photoprism/photoprism/internal/get"
|
||||||
|
"github.com/photoprism/photoprism/pkg/authn"
|
||||||
|
"github.com/photoprism/photoprism/pkg/header"
|
||||||
|
"github.com/photoprism/photoprism/pkg/i18n"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OAuthRedirect creates a new access token for authenticated clients and then redirects back to the application.
|
||||||
|
//
|
||||||
|
// GET /api/v1/oauth/redirect
|
||||||
|
func OAuthRedirect(router *gin.RouterGroup) {
|
||||||
|
router.GET("/oauth/redirect", func(c *gin.Context) {
|
||||||
|
// Prevent CDNs from caching this endpoint.
|
||||||
|
if header.IsCdn(c.Request) {
|
||||||
|
AbortNotFound(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable caching of responses.
|
||||||
|
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||||
|
|
||||||
|
// Get client IP address for logs and rate limiting checks.
|
||||||
|
clientIp := ClientIP(c)
|
||||||
|
actor := "unknown client"
|
||||||
|
action := "redirect"
|
||||||
|
|
||||||
|
// Abort if running in public mode.
|
||||||
|
if get.Config().Public() {
|
||||||
|
event.AuditErr([]string{clientIp, "oauth2", actor, action, authn.ErrDisabledInPublicMode.Error()})
|
||||||
|
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Send response.
|
||||||
|
c.JSON(http.StatusOK, gin.H{"status": StatusSuccess})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -18,10 +18,10 @@ import (
|
|||||||
"github.com/photoprism/photoprism/pkg/rnd"
|
"github.com/photoprism/photoprism/pkg/rnd"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RevokeOAuthToken takes an access token and deletes it. A client may only delete its own tokens.
|
// OAuthRevoke takes an access token and deletes it. A client may only delete its own tokens.
|
||||||
//
|
//
|
||||||
// POST /api/v1/oauth/revoke
|
// POST /api/v1/oauth/revoke
|
||||||
func RevokeOAuthToken(router *gin.RouterGroup) {
|
func OAuthRevoke(router *gin.RouterGroup) {
|
||||||
router.POST("/oauth/revoke", func(c *gin.Context) {
|
router.POST("/oauth/revoke", func(c *gin.Context) {
|
||||||
// Prevent CDNs from caching this endpoint.
|
// Prevent CDNs from caching this endpoint.
|
||||||
if header.IsCdn(c.Request) {
|
if header.IsCdn(c.Request) {
|
||||||
@@ -7,8 +7,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/pkg/rnd"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
@@ -16,9 +14,10 @@ import (
|
|||||||
"github.com/photoprism/photoprism/internal/entity"
|
"github.com/photoprism/photoprism/internal/entity"
|
||||||
"github.com/photoprism/photoprism/internal/form"
|
"github.com/photoprism/photoprism/internal/form"
|
||||||
"github.com/photoprism/photoprism/pkg/header"
|
"github.com/photoprism/photoprism/pkg/header"
|
||||||
|
"github.com/photoprism/photoprism/pkg/rnd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRevokeOAuthToken(t *testing.T) {
|
func TestOAuthRevoke(t *testing.T) {
|
||||||
const tokenPath = "/api/v1/oauth/token"
|
const tokenPath = "/api/v1/oauth/token"
|
||||||
const revokePath = "/api/v1/oauth/revoke"
|
const revokePath = "/api/v1/oauth/revoke"
|
||||||
|
|
||||||
@@ -27,8 +26,8 @@ func TestRevokeOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
RevokeOAuthToken(router)
|
OAuthRevoke(router)
|
||||||
|
|
||||||
data := url.Values{
|
data := url.Values{
|
||||||
"grant_type": {"client_credentials"},
|
"grant_type": {"client_credentials"},
|
||||||
@@ -63,8 +62,8 @@ func TestRevokeOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
RevokeOAuthToken(router)
|
OAuthRevoke(router)
|
||||||
|
|
||||||
createData := url.Values{
|
createData := url.Values{
|
||||||
"grant_type": {"client_credentials"},
|
"grant_type": {"client_credentials"},
|
||||||
@@ -104,8 +103,8 @@ func TestRevokeOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
RevokeOAuthToken(router)
|
OAuthRevoke(router)
|
||||||
|
|
||||||
createData := url.Values{
|
createData := url.Values{
|
||||||
"grant_type": {"client_credentials"},
|
"grant_type": {"client_credentials"},
|
||||||
@@ -145,8 +144,8 @@ func TestRevokeOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
RevokeOAuthToken(router)
|
OAuthRevoke(router)
|
||||||
|
|
||||||
createData := url.Values{
|
createData := url.Values{
|
||||||
"grant_type": {"client_credentials"},
|
"grant_type": {"client_credentials"},
|
||||||
@@ -186,8 +185,8 @@ func TestRevokeOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
RevokeOAuthToken(router)
|
OAuthRevoke(router)
|
||||||
|
|
||||||
createData := url.Values{
|
createData := url.Values{
|
||||||
"grant_type": {"client_credentials"},
|
"grant_type": {"client_credentials"},
|
||||||
@@ -225,7 +224,7 @@ func TestRevokeOAuthToken(t *testing.T) {
|
|||||||
t.Run("PublicMode", func(t *testing.T) {
|
t.Run("PublicMode", func(t *testing.T) {
|
||||||
app, router, _ := NewApiTest()
|
app, router, _ := NewApiTest()
|
||||||
|
|
||||||
RevokeOAuthToken(router)
|
OAuthRevoke(router)
|
||||||
|
|
||||||
sess := entity.SessionFixtures.Get("alice_token")
|
sess := entity.SessionFixtures.Get("alice_token")
|
||||||
|
|
||||||
@@ -246,8 +245,8 @@ func TestRevokeOAuthToken(t *testing.T) {
|
|||||||
|
|
||||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
RevokeOAuthToken(router)
|
OAuthRevoke(router)
|
||||||
|
|
||||||
data := url.Values{
|
data := url.Values{
|
||||||
"grant_type": {"password"},
|
"grant_type": {"password"},
|
||||||
@@ -18,11 +18,10 @@ import (
|
|||||||
"github.com/photoprism/photoprism/pkg/header"
|
"github.com/photoprism/photoprism/pkg/header"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateOAuthToken creates a new access token for clients that
|
// OAuthToken creates a new access token for clients that authenticate with valid OAuth2 client credentials.
|
||||||
// authenticate with valid OAuth2 client credentials.
|
|
||||||
//
|
//
|
||||||
// POST /api/v1/oauth/token
|
// POST /api/v1/oauth/token
|
||||||
func CreateOAuthToken(router *gin.RouterGroup) {
|
func OAuthToken(router *gin.RouterGroup) {
|
||||||
router.POST("/oauth/token", func(c *gin.Context) {
|
router.POST("/oauth/token", func(c *gin.Context) {
|
||||||
// Prevent CDNs from caching this endpoint.
|
// Prevent CDNs from caching this endpoint.
|
||||||
if header.IsCdn(c.Request) {
|
if header.IsCdn(c.Request) {
|
||||||
@@ -7,19 +7,20 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/config"
|
"github.com/photoprism/photoprism/internal/config"
|
||||||
"github.com/photoprism/photoprism/pkg/authn"
|
"github.com/photoprism/photoprism/pkg/authn"
|
||||||
"github.com/photoprism/photoprism/pkg/header"
|
"github.com/photoprism/photoprism/pkg/header"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateOAuthToken(t *testing.T) {
|
func TestOAuthToken(t *testing.T) {
|
||||||
t.Run("ClientSuccess", func(t *testing.T) {
|
t.Run("ClientSuccess", func(t *testing.T) {
|
||||||
app, router, conf := NewApiTest()
|
app, router, conf := NewApiTest()
|
||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -44,7 +45,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
t.Run("PublicMode", func(t *testing.T) {
|
t.Run("PublicMode", func(t *testing.T) {
|
||||||
app, router, _ := NewApiTest()
|
app, router, _ := NewApiTest()
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -71,7 +72,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -98,7 +99,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -124,7 +125,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -151,7 +152,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -178,7 +179,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -204,7 +205,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -234,7 +235,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
|
|
||||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -263,7 +264,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
conf.SetAuthMode(config.AuthModePasswd)
|
conf.SetAuthMode(config.AuthModePasswd)
|
||||||
defer conf.SetAuthMode(config.AuthModePublic)
|
defer conf.SetAuthMode(config.AuthModePublic)
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -294,7 +295,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
|
|
||||||
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
sessId := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -325,7 +326,7 @@ func TestCreateOAuthToken(t *testing.T) {
|
|||||||
|
|
||||||
sessId := AuthenticateUser(app, router, "deleted", "Deleted123!")
|
sessId := AuthenticateUser(app, router, "deleted", "Deleted123!")
|
||||||
|
|
||||||
CreateOAuthToken(router)
|
OAuthToken(router)
|
||||||
|
|
||||||
var method = "POST"
|
var method = "POST"
|
||||||
var path = "/api/v1/oauth/token"
|
var path = "/api/v1/oauth/token"
|
||||||
@@ -16,8 +16,12 @@ func TestMain(m *testing.M) {
|
|||||||
c := config.TestConfig()
|
c := config.TestConfig()
|
||||||
defer c.CloseDb()
|
defer c.CloseDb()
|
||||||
|
|
||||||
|
// Run unit tests.
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
|
// Close database connection.
|
||||||
|
_ = c.CloseDb()
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
c := config.NewTestConfig("commands")
|
c := config.NewTestConfig("commands")
|
||||||
get.SetConfig(c)
|
get.SetConfig(c)
|
||||||
|
defer c.CloseDb()
|
||||||
|
|
||||||
InitConfig = func(ctx *cli.Context) (*config.Config, error) {
|
InitConfig = func(ctx *cli.Context) (*config.Config, error) {
|
||||||
return c, c.Init()
|
return c, c.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run unit tests.
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ var ConfigReports = []Report{
|
|||||||
{Title: "Global Config Options", NoWrap: true, Report: func(conf *config.Config) ([][]string, []string) {
|
{Title: "Global Config Options", NoWrap: true, Report: func(conf *config.Config) ([][]string, []string) {
|
||||||
return conf.Report()
|
return conf.Report()
|
||||||
}},
|
}},
|
||||||
{Title: "OpenID Connect", NoWrap: true, Report: func(conf *config.Config) ([][]string, []string) {
|
{Title: "OpenID Connect (OIDC)", NoWrap: true, Report: func(conf *config.Config) ([][]string, []string) {
|
||||||
return conf.OIDCReport()
|
return conf.OIDCReport()
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,21 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/config"
|
|
||||||
"github.com/photoprism/photoprism/pkg/capture"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/capture"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShowConfigOptionsCommand(t *testing.T) {
|
func TestShowConfigOptionsCommand(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ctx := config.CliTestContext()
|
ctx := NewTestContext(nil)
|
||||||
|
|
||||||
output := capture.Output(func() {
|
output := capture.Output(func() {
|
||||||
err = ShowConfigOptionsCommand.Run(ctx)
|
err = ShowConfigOptionsCommand.Run(ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
assert.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Contains(t, output, "PHOTOPRISM_IMPORT_PATH")
|
assert.Contains(t, output, "PHOTOPRISM_IMPORT_PATH")
|
||||||
assert.Contains(t, output, "--sidecar-path")
|
assert.Contains(t, output, "--sidecar-path")
|
||||||
assert.Contains(t, output, "sidecar `PATH` *optional*")
|
assert.Contains(t, output, "sidecar `PATH` *optional*")
|
||||||
|
|||||||
@@ -11,26 +11,26 @@ const OIDCDefaultScopes = "openid email profile"
|
|||||||
|
|
||||||
// OIDCEnabled checks if login via OpenID Connect (OIDC) is enabled.
|
// OIDCEnabled checks if login via OpenID Connect (OIDC) is enabled.
|
||||||
func (c *Config) OIDCEnabled() bool {
|
func (c *Config) OIDCEnabled() bool {
|
||||||
return c.options.OIDCIssuer != "" && c.options.OIDCClient != "" && c.options.OIDCSecret != ""
|
return c.options.OIDCUri != "" && c.options.OIDCClient != "" && c.options.OIDCSecret != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// OIDCIssuer returns the OpenID Connect Issuer URL as string for single sign-on via OIDC.
|
// OIDCUri returns the OpenID Connect issuer URI as *url.URL for single sign-on via OIDC.
|
||||||
func (c *Config) OIDCIssuer() string {
|
func (c *Config) OIDCUri() *url.URL {
|
||||||
return c.options.OIDCIssuer
|
if uri := c.options.OIDCUri; uri == "" {
|
||||||
}
|
|
||||||
|
|
||||||
// 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{}
|
return &url.URL{}
|
||||||
} else if result, err := url.Parse(oidcIssuer); err != nil {
|
} else if result, err := url.Parse(uri); err != nil {
|
||||||
log.Errorf("oidc: failed to parse issuer URL (%s)", err)
|
log.Errorf("oidc: failed to parse issuer URI (%s)", err)
|
||||||
return &url.URL{}
|
return &url.URL{}
|
||||||
} else {
|
} else {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OIDCInsecure checks if OIDC issuer SSL/TLS certificate verification should be skipped.
|
||||||
|
func (c *Config) OIDCInsecure() bool {
|
||||||
|
return c.options.OIDCInsecure
|
||||||
|
}
|
||||||
|
|
||||||
// OIDCClient returns the Client ID for single sign-on via OIDC.
|
// OIDCClient returns the Client ID for single sign-on via OIDC.
|
||||||
func (c *Config) OIDCClient() string {
|
func (c *Config) OIDCClient() string {
|
||||||
return c.options.OIDCClient
|
return c.options.OIDCClient
|
||||||
@@ -50,9 +50,14 @@ func (c *Config) OIDCScopes() string {
|
|||||||
return c.options.OIDCScopes
|
return c.options.OIDCScopes
|
||||||
}
|
}
|
||||||
|
|
||||||
// OIDCInsecure checks if OIDC issuer SSL/TLS certificate verification should be skipped.
|
// OIDCIcon returns the custom issuer icon URI for single sign-on via OIDC, if any.
|
||||||
func (c *Config) OIDCInsecure() bool {
|
func (c *Config) OIDCIcon() string {
|
||||||
return c.options.OIDCInsecure
|
return c.options.OIDCIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDCButton returns the custom button text for single sign-on via OIDC, if any.
|
||||||
|
func (c *Config) OIDCButton() string {
|
||||||
|
return c.options.OIDCButton
|
||||||
}
|
}
|
||||||
|
|
||||||
// OIDCRegister checks if new accounts may be created via OIDC.
|
// OIDCRegister checks if new accounts may be created via OIDC.
|
||||||
@@ -60,17 +65,25 @@ func (c *Config) OIDCRegister() bool {
|
|||||||
return c.options.OIDCRegister
|
return c.options.OIDCRegister
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OIDCRedirect checks if unauthenticated users should automatically be redirected to the OIDC login page.
|
||||||
|
func (c *Config) OIDCRedirect() bool {
|
||||||
|
return c.options.OIDCRedirect
|
||||||
|
}
|
||||||
|
|
||||||
// OIDCReport returns the OpenID Connect config values as a table for reporting.
|
// OIDCReport returns the OpenID Connect config values as a table for reporting.
|
||||||
func (c *Config) OIDCReport() (rows [][]string, cols []string) {
|
func (c *Config) OIDCReport() (rows [][]string, cols []string) {
|
||||||
cols = []string{"Name", "Value"}
|
cols = []string{"Name", "Value"}
|
||||||
|
|
||||||
rows = [][]string{
|
rows = [][]string{
|
||||||
{"oidc-issuer", c.OIDCIssuer()},
|
{"oidc-uri", c.OIDCUri().String()},
|
||||||
|
{"oidc-insecure", fmt.Sprintf("%t", c.OIDCInsecure())},
|
||||||
{"oidc-client", c.OIDCClient()},
|
{"oidc-client", c.OIDCClient()},
|
||||||
{"oidc-secret", strings.Repeat("*", utf8.RuneCountInString(c.OIDCSecret()))},
|
{"oidc-secret", strings.Repeat("*", utf8.RuneCountInString(c.OIDCSecret()))},
|
||||||
{"oidc-scopes", c.OIDCScopes()},
|
{"oidc-scopes", c.OIDCScopes()},
|
||||||
{"oidc-insecure", fmt.Sprintf("%t", c.OIDCInsecure())},
|
{"oidc-icon", c.OIDCIcon()},
|
||||||
|
{"oidc-button", c.OIDCButton()},
|
||||||
{"oidc-register", fmt.Sprintf("%t", c.OIDCRegister())},
|
{"oidc-register", fmt.Sprintf("%t", c.OIDCRegister())},
|
||||||
|
{"oidc-redirect", fmt.Sprintf("%t", c.OIDCRedirect())},
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows, cols
|
return rows, cols
|
||||||
|
|||||||
@@ -13,20 +13,24 @@ func TestConfig_OIDCEnabled(t *testing.T) {
|
|||||||
assert.False(t, c.OIDCEnabled())
|
assert.False(t, c.OIDCEnabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_OIDCIssuer(t *testing.T) {
|
func TestConfig_OIDCUri(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
assert.Equal(t, "", c.OIDCIssuer())
|
assert.IsType(t, &url.URL{}, c.OIDCUri())
|
||||||
|
assert.Equal(t, "", c.OIDCUri().Path)
|
||||||
|
|
||||||
|
c.options.OIDCUri = "test"
|
||||||
|
assert.Equal(t, "test", c.OIDCUri().Path)
|
||||||
|
c.options.OIDCUri = ""
|
||||||
|
|
||||||
|
assert.IsType(t, &url.URL{}, c.OIDCUri())
|
||||||
|
assert.Equal(t, "", c.OIDCUri().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_OIDCIssuerURL(t *testing.T) {
|
func TestConfig_OIDCInsecure(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
assert.IsType(t, &url.URL{}, c.OIDCIssuerURL())
|
assert.False(t, c.OIDCInsecure())
|
||||||
assert.Equal(t, "", c.OIDCIssuerURL().Path)
|
|
||||||
|
|
||||||
c.options.OIDCIssuer = "test"
|
|
||||||
assert.Equal(t, "test", c.OIDCIssuerURL().Path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_OIDCClient(t *testing.T) {
|
func TestConfig_OIDCClient(t *testing.T) {
|
||||||
@@ -51,16 +55,28 @@ func TestConfig_OIDCScopes(t *testing.T) {
|
|||||||
assert.Equal(t, OIDCDefaultScopes, c.OIDCScopes())
|
assert.Equal(t, OIDCDefaultScopes, c.OIDCScopes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfig_OIDCIcon(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.OIDCIcon())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_OIDCButton(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.OIDCButton())
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfig_OIDCRegister(t *testing.T) {
|
func TestConfig_OIDCRegister(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
assert.False(t, c.OIDCRegister())
|
assert.False(t, c.OIDCRegister())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_OIDCInsecure(t *testing.T) {
|
func TestConfig_OIDCRedirect(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
assert.False(t, c.OIDCInsecure())
|
assert.False(t, c.OIDCRedirect())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_OIDCReport(t *testing.T) {
|
func TestConfig_OIDCReport(t *testing.T) {
|
||||||
|
|||||||
@@ -20,11 +20,10 @@ func TestMain(m *testing.M) {
|
|||||||
log.SetLevel(logrus.TraceLevel)
|
log.SetLevel(logrus.TraceLevel)
|
||||||
|
|
||||||
c := TestConfig()
|
c := TestConfig()
|
||||||
|
defer c.CloseDb()
|
||||||
|
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
_ = c.CloseDb()
|
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,15 @@ var Flags = CliFlags{
|
|||||||
EnvVar: EnvVar("ADMIN_PASSWORD"),
|
EnvVar: EnvVar("ADMIN_PASSWORD"),
|
||||||
}}, {
|
}}, {
|
||||||
Flag: cli.StringFlag{
|
Flag: cli.StringFlag{
|
||||||
Name: "oidc-issuer",
|
Name: "oidc-uri",
|
||||||
Usage: "server `URI` for single sign-on via OpenID Connect (OIDC)",
|
Usage: "issuer `URI` for single sign-on via OpenID Connect (OIDC), e.g. https://accounts.google.com/o/oauth2/v2/auth",
|
||||||
Value: "",
|
Value: "",
|
||||||
EnvVar: EnvVar("OIDC_ISSUER"),
|
EnvVar: EnvVar("OIDC_URI"),
|
||||||
|
}}, {
|
||||||
|
Flag: cli.BoolFlag{
|
||||||
|
Name: "oidc-insecure",
|
||||||
|
Usage: "skip issuer SSL/TLS certificate verification",
|
||||||
|
EnvVar: EnvVar("OIDC_INSECURE"),
|
||||||
}}, {
|
}}, {
|
||||||
Flag: cli.StringFlag{
|
Flag: cli.StringFlag{
|
||||||
Name: "oidc-client",
|
Name: "oidc-client",
|
||||||
@@ -68,16 +73,28 @@ var Flags = CliFlags{
|
|||||||
Value: OIDCDefaultScopes,
|
Value: OIDCDefaultScopes,
|
||||||
EnvVar: EnvVar("OIDC_SCOPES"),
|
EnvVar: EnvVar("OIDC_SCOPES"),
|
||||||
}}, {
|
}}, {
|
||||||
Flag: cli.BoolFlag{
|
Flag: cli.StringFlag{
|
||||||
Name: "oidc-insecure",
|
Name: "oidc-icon",
|
||||||
Usage: "skip issuer SSL/TLS certificate verification",
|
Usage: "custom issuer icon `URI` for single sign-on via OIDC",
|
||||||
EnvVar: EnvVar("OIDC_INSECURE"),
|
Value: "",
|
||||||
|
EnvVar: EnvVar("OIDC_ICON"),
|
||||||
|
}}, {
|
||||||
|
Flag: cli.StringFlag{
|
||||||
|
Name: "oidc-button",
|
||||||
|
Usage: "custom login button `TEXT` for single sign-on via OIDC",
|
||||||
|
Value: "",
|
||||||
|
EnvVar: EnvVar("OIDC_BUTTON"),
|
||||||
}}, {
|
}}, {
|
||||||
Flag: cli.BoolFlag{
|
Flag: cli.BoolFlag{
|
||||||
Name: "oidc-register",
|
Name: "oidc-register",
|
||||||
Usage: "allow user registration via OIDC",
|
Usage: "allow user registration via OIDC",
|
||||||
EnvVar: EnvVar("OIDC_REGISTER"),
|
EnvVar: EnvVar("OIDC_REGISTER"),
|
||||||
}}, {
|
}}, {
|
||||||
|
Flag: cli.BoolFlag{
|
||||||
|
Name: "oidc-redirect",
|
||||||
|
Usage: "automatically redirect unauthenticated users to the OIDC login page",
|
||||||
|
EnvVar: EnvVar("OIDC_REDIRECT"),
|
||||||
|
}}, {
|
||||||
Flag: cli.Int64Flag{
|
Flag: cli.Int64Flag{
|
||||||
Name: "session-maxage",
|
Name: "session-maxage",
|
||||||
Value: DefaultSessionMaxAge,
|
Value: DefaultSessionMaxAge,
|
||||||
|
|||||||
@@ -31,12 +31,15 @@ type Options struct {
|
|||||||
PasswordResetUri string `yaml:"PasswordResetUri" json:"-" flag:"password-reset-uri"`
|
PasswordResetUri string `yaml:"PasswordResetUri" json:"-" flag:"password-reset-uri"`
|
||||||
RegisterUri string `yaml:"RegisterUri" json:"-" flag:"register-uri"`
|
RegisterUri string `yaml:"RegisterUri" json:"-" flag:"register-uri"`
|
||||||
LoginUri string `yaml:"LoginUri" json:"-" flag:"login-uri"`
|
LoginUri string `yaml:"LoginUri" json:"-" flag:"login-uri"`
|
||||||
OIDCIssuer string `yaml:"OIDCIssuer" json:"OIDCIssuer" flag:"oidc-issuer"`
|
OIDCUri string `yaml:"OIDCUri" json:"OIDCUri" flag:"oidc-uri"`
|
||||||
|
OIDCInsecure bool `yaml:"OIDCInsecure" json:"-" flag:"oidc-insecure"`
|
||||||
OIDCClient string `yaml:"OIDCClient" json:"-" flag:"oidc-client"`
|
OIDCClient string `yaml:"OIDCClient" json:"-" flag:"oidc-client"`
|
||||||
OIDCSecret string `yaml:"OIDCSecret" json:"-" flag:"oidc-secret"`
|
OIDCSecret string `yaml:"OIDCSecret" json:"-" flag:"oidc-secret"`
|
||||||
OIDCScopes string `yaml:"OIDCScopes" json:"-" flag:"oidc-scopes"`
|
OIDCScopes string `yaml:"OIDCScopes" json:"-" flag:"oidc-scopes"`
|
||||||
OIDCInsecure bool `yaml:"OIDCInsecure" json:"-" flag:"oidc-insecure"`
|
OIDCIcon string `yaml:"OIDCIcon" json:"-" flag:"oidc-icon"`
|
||||||
|
OIDCButton string `yaml:"OIDCButton" json:"-" flag:"oidc-button"`
|
||||||
OIDCRegister bool `yaml:"OIDCRegister" json:"-" flag:"oidc-register"`
|
OIDCRegister bool `yaml:"OIDCRegister" json:"-" flag:"oidc-register"`
|
||||||
|
OIDCRedirect bool `yaml:"OIDCRedirect" json:"-" flag:"oidc-redirect"`
|
||||||
SessionMaxAge int64 `yaml:"SessionMaxAge" json:"-" flag:"session-maxage"`
|
SessionMaxAge int64 `yaml:"SessionMaxAge" json:"-" flag:"session-maxage"`
|
||||||
SessionTimeout int64 `yaml:"SessionTimeout" json:"-" flag:"session-timeout"`
|
SessionTimeout int64 `yaml:"SessionTimeout" json:"-" flag:"session-timeout"`
|
||||||
SessionCache int64 `yaml:"SessionCache" json:"-" flag:"session-cache"`
|
SessionCache int64 `yaml:"SessionCache" json:"-" flag:"session-cache"`
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ func CliTestContext() *cli.Context {
|
|||||||
globalSet := flag.NewFlagSet("test", 0)
|
globalSet := flag.NewFlagSet("test", 0)
|
||||||
globalSet.String("config-path", config.ConfigPath, "doc")
|
globalSet.String("config-path", config.ConfigPath, "doc")
|
||||||
globalSet.String("admin-password", config.DarktableBin, "doc")
|
globalSet.String("admin-password", config.DarktableBin, "doc")
|
||||||
globalSet.String("oidc-issuer", config.OIDCIssuer, "doc")
|
globalSet.String("oidc-uri", config.OIDCUri, "doc")
|
||||||
globalSet.String("oidc-client", config.OIDCClient, "doc")
|
globalSet.String("oidc-client", config.OIDCClient, "doc")
|
||||||
globalSet.String("oidc-secret", config.OIDCSecret, "doc")
|
globalSet.String("oidc-secret", config.OIDCSecret, "doc")
|
||||||
globalSet.String("oidc-scopes", config.OIDCScopes, "doc")
|
globalSet.String("oidc-scopes", config.OIDCScopes, "doc")
|
||||||
@@ -263,7 +263,7 @@ func CliTestContext() *cli.Context {
|
|||||||
|
|
||||||
LogErr(c.Set("config-path", config.ConfigPath))
|
LogErr(c.Set("config-path", config.ConfigPath))
|
||||||
LogErr(c.Set("admin-password", config.AdminPassword))
|
LogErr(c.Set("admin-password", config.AdminPassword))
|
||||||
LogErr(c.Set("oidc-issuer", config.OIDCIssuer))
|
LogErr(c.Set("oidc-uri", config.OIDCUri))
|
||||||
LogErr(c.Set("oidc-client", config.OIDCClient))
|
LogErr(c.Set("oidc-client", config.OIDCClient))
|
||||||
LogErr(c.Set("oidc-secret", config.OIDCSecret))
|
LogErr(c.Set("oidc-secret", config.OIDCSecret))
|
||||||
LogErr(c.Set("oidc-scopes", OIDCDefaultScopes))
|
LogErr(c.Set("oidc-scopes", OIDCDefaultScopes))
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ var onceOidc sync.Once
|
|||||||
|
|
||||||
func initOidc() {
|
func initOidc() {
|
||||||
services.OIDC, _ = oidc.NewClient(
|
services.OIDC, _ = oidc.NewClient(
|
||||||
Config().OIDCIssuerURL(),
|
Config().OIDCUri(),
|
||||||
Config().OIDCClient(),
|
Config().OIDCClient(),
|
||||||
Config().OIDCSecret(),
|
Config().OIDCSecret(),
|
||||||
Config().OIDCScopes(),
|
Config().OIDCScopes(),
|
||||||
@@ -20,7 +20,7 @@ func initOidc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func OIDC() *oidc.Client {
|
func OIDC() *oidc.Client {
|
||||||
oncePhotos.Do(initOidc)
|
onceOidc.Do(initOidc)
|
||||||
|
|
||||||
return services.OIDC
|
return services.OIDC
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,9 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
c := config.NewTestConfig("photoprism")
|
c := config.NewTestConfig("photoprism")
|
||||||
SetConfig(c)
|
SetConfig(c)
|
||||||
|
defer c.CloseDb()
|
||||||
|
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
_ = c.CloseDb()
|
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,11 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
|
|||||||
api.CreateSession(APIv1)
|
api.CreateSession(APIv1)
|
||||||
api.GetSession(APIv1)
|
api.GetSession(APIv1)
|
||||||
api.DeleteSession(APIv1)
|
api.DeleteSession(APIv1)
|
||||||
api.CreateOAuthToken(APIv1)
|
api.OAuthAuthorize(APIv1)
|
||||||
api.RevokeOAuthToken(APIv1)
|
api.OAuthRedirect(APIv1)
|
||||||
|
api.OAuthToken(APIv1)
|
||||||
|
api.OAuthRevoke(APIv1)
|
||||||
|
api.OAuthLogout(APIv1)
|
||||||
|
|
||||||
// Server Config.
|
// Server Config.
|
||||||
api.GetConfigOptions(APIv1)
|
api.GetConfigOptions(APIv1)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ func TestMain(m *testing.M) {
|
|||||||
// Init test config.
|
// Init test config.
|
||||||
c := config.TestConfig()
|
c := config.TestConfig()
|
||||||
get.SetConfig(c)
|
get.SetConfig(c)
|
||||||
|
defer c.CloseDb()
|
||||||
|
|
||||||
// Increase login rate limit for testing.
|
// Increase login rate limit for testing.
|
||||||
limiter.Login = limiter.NewLimit(1, 10000)
|
limiter.Login = limiter.NewLimit(1, 10000)
|
||||||
@@ -28,8 +29,5 @@ func TestMain(m *testing.M) {
|
|||||||
// Run unit tests.
|
// Run unit tests.
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
// Close database connection.
|
|
||||||
_ = c.CloseDb()
|
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ func TestMain(m *testing.M) {
|
|||||||
log.SetLevel(logrus.TraceLevel)
|
log.SetLevel(logrus.TraceLevel)
|
||||||
event.AuditLog = log
|
event.AuditLog = log
|
||||||
|
|
||||||
|
defer Shutdown()
|
||||||
|
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
|
|
||||||
// Remove generated test files and folders.
|
// Remove generated test files and folders.
|
||||||
@@ -22,8 +24,6 @@ func TestMain(m *testing.M) {
|
|||||||
_ = os.RemoveAll("testdata/cache")
|
_ = os.RemoveAll("testdata/cache")
|
||||||
_ = os.RemoveAll("testdata/vips")
|
_ = os.RemoveAll("testdata/vips")
|
||||||
|
|
||||||
Shutdown()
|
|
||||||
|
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user