Skip to content

Commit 6e43f14

Browse files
committed
Auth: Adjust JWT default scope and ACL, add tests photoprism#5230
Signed-off-by: Michael Mayer <[email protected]>
1 parent e1e673b commit 6e43f14

File tree

6 files changed

+66
-2
lines changed

6 files changed

+66
-2
lines changed

internal/api/api_auth_jwt_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,39 @@ func TestAuthAnyJWT(t *testing.T) {
9191
assert.True(t, session.SessExpires > session.CreatedAt.Unix())
9292
assert.True(t, session.LastActive >= session.CreatedAt.Unix())
9393
})
94+
t.Run("ConfigScopePortalRole", func(t *testing.T) {
95+
fx := newPortalJWTFixture(t, "cluster-jwt-config")
96+
spec := fx.defaultClaimsSpec()
97+
spec.Scope = []string{"cluster", "config"}
98+
token := fx.issue(t, spec)
99+
100+
origScope := fx.nodeConf.Options().JWTScope
101+
fx.nodeConf.Options().JWTScope = "cluster config"
102+
get.SetConfig(fx.nodeConf)
103+
t.Cleanup(func() {
104+
fx.nodeConf.Options().JWTScope = origScope
105+
get.SetConfig(fx.nodeConf)
106+
})
107+
108+
gin.SetMode(gin.TestMode)
109+
w := httptest.NewRecorder()
110+
c, _ := gin.CreateTestContext(w)
111+
req, _ := http.NewRequest(http.MethodGet, "/api/v1/config", nil)
112+
req.Header.Set("Authorization", "Bearer "+token)
113+
req.Header.Set(header.UserAgent, "PhotoPrism Portal/1.0")
114+
req.RemoteAddr = "192.0.2.51:1234"
115+
c.Request = req
116+
117+
session := authAnyJWT(c, "192.0.2.51", token, acl.ResourceConfig, acl.Permissions{acl.ActionView})
118+
require.NotNil(t, session)
119+
assert.Equal(t, http.StatusOK, session.HttpStatus())
120+
assert.Equal(t, acl.RolePortal, session.GetClientRole())
121+
assert.True(t, session.Valid())
122+
123+
cfg := fx.nodeConf.ClientSession(session)
124+
require.NotNil(t, cfg)
125+
assert.Equal(t, string(config.ClientUser), cfg.Mode)
126+
})
94127
t.Run("ClusterCIDRAllowed", func(t *testing.T) {
95128
fx := newPortalJWTFixture(t, "cluster-jwt-cidr-allow")
96129
spec := fx.defaultClaimsSpec()

internal/api/api_client_config_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package api
22

33
import (
44
"net/http"
5+
"net/http/httptest"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
89
"github.com/tidwall/gjson"
910

11+
"github.com/photoprism/photoprism/internal/auth/acl"
1012
"github.com/photoprism/photoprism/internal/config"
13+
"github.com/photoprism/photoprism/pkg/http/header"
1114
)
1215

1316
func TestGetClientConfig(t *testing.T) {
@@ -39,4 +42,29 @@ func TestGetClientConfig(t *testing.T) {
3942
assert.Equal(t, http.StatusUnauthorized, r.Code)
4043
conf.Options().DisableFrontend = false
4144
})
45+
t.Run("PortalJWT", func(t *testing.T) {
46+
fx := newPortalJWTFixture(t, "client-config-handler")
47+
48+
app, router, conf := NewApiTest()
49+
conf.SetAuthMode(config.AuthModePasswd)
50+
defer conf.SetAuthMode(config.AuthModePublic)
51+
52+
GetClientConfig(router)
53+
54+
spec := fx.defaultClaimsSpec()
55+
spec.Scope = []string{acl.ResourceCluster.String(), acl.ResourceConfig.String()}
56+
57+
token := fx.issue(t, spec)
58+
59+
req, _ := http.NewRequest(http.MethodGet, "/api/v1/config", nil)
60+
req.RemoteAddr = "10.10.0.5:1234"
61+
header.SetAuthorization(req, token)
62+
req.Header.Set(header.UserAgent, "PhotoPrism Portal/1.0")
63+
64+
w := httptest.NewRecorder()
65+
app.ServeHTTP(w, req)
66+
67+
assert.Equal(t, http.StatusOK, w.Code)
68+
assert.Equal(t, "user", gjson.Get(w.Body.String(), "mode").String())
69+
})
4270
}

internal/auth/acl/rules.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ var Rules = ACL{
5858
},
5959
ResourceConfig: Roles{
6060
RoleAdmin: GrantFullAccess,
61+
RolePortal: GrantFullAccess,
6162
RoleClient: GrantViewOwn,
6263
RoleDefault: GrantViewOwn,
6364
},

internal/config/config_cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
// DefaultPortalUrl specifies the default portal URL with variable cluster domain.
2424
var DefaultPortalUrl = "https://portal.${PHOTOPRISM_CLUSTER_DOMAIN}"
2525
var DefaultNodeRole = cluster.RoleInstance
26-
var DefaultJWTAllowedScopes = "cluster vision metrics"
26+
var DefaultJWTAllowedScopes = "config cluster vision metrics"
2727

2828
// ClusterDomain returns the cluster DOMAIN (lowercase DNS name; 1–63 chars).
2929
func (c *Config) ClusterDomain() string {

internal/config/config_cluster_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func TestConfig_Cluster(t *testing.T) {
181181
c.options.JWTScope = "cluster vision"
182182
assert.Equal(t, list.ParseAttr("cluster vision"), c.JWTAllowedScopes())
183183
c.options.JWTScope = ""
184-
assert.Equal(t, list.ParseAttr("cluster vision metrics"), c.JWTAllowedScopes())
184+
assert.Equal(t, list.ParseAttr("config cluster vision metrics"), c.JWTAllowedScopes())
185185
})
186186
t.Run("Paths", func(t *testing.T) {
187187
c := NewConfig(CliTestContext())

internal/config/test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ func NewTestOptionsForPath(dbName, dataPath string) *Options {
180180
DatabaseDriver: driver,
181181
DatabaseDSN: dsn,
182182
AdminPassword: "photoprism",
183+
ClusterCIDR: "",
184+
JWTScope: DefaultJWTAllowedScopes,
183185
OriginalsLimit: 66,
184186
ResolutionLimit: 33,
185187
VisionApi: true,

0 commit comments

Comments
 (0)