mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-11-08 21:43:00 -05:00
Merge branch 'origin/main' into 'next-release/main'
This commit is contained in:
commit
13fde6710d
7 files changed
+121
-4
No files matched your search
@@ -223,6 +223,11 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
user, err = m.userRoleAssigner.UpdateUserRoleAssignment(ctx, user, claims)
|
||||
if err != nil {
|
||||
m.logger.Error().Err(err).Msg("Could not get user roles")
|
||||
if errors.Is(err, userroles.ErrRoleClaim) {
|
||||
// the token has no usable role claim: the user is not authorized, OpenCloud did not fail
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -16,6 +18,7 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/services/proxy/pkg/router"
|
||||
"github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend"
|
||||
"github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend/mocks"
|
||||
"github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles"
|
||||
userRoleMocks "github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles/mocks"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/auth/scope"
|
||||
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
@@ -466,3 +469,54 @@ func TestTenantIDMapping(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleAssignmentErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
roleErr error
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "role claim missing is forbidden",
|
||||
roleErr: fmt.Errorf("%w: unsupported type '<nil>'", userroles.ErrRoleClaim),
|
||||
wantStatusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "settings service failure stays an internal error",
|
||||
roleErr: errors.New("settings service unavailable"),
|
||||
wantStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user := &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "user-id"}}
|
||||
|
||||
ub := mocks.UserBackend{}
|
||||
ub.On("GetUserByClaims", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(user, "", nil)
|
||||
|
||||
ra := userRoleMocks.UserRoleAssigner{}
|
||||
ra.On("UpdateUserRoleAssignment", mock.Anything, mock.Anything, mock.Anything).Return(nil, tc.roleErr)
|
||||
|
||||
called := false
|
||||
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) { called = true })
|
||||
sut := AccountResolver(
|
||||
Logger(log.NewLogger()),
|
||||
UserProvider(&ub),
|
||||
UserRoleAssigner(&ra),
|
||||
SkipUserInfo(false),
|
||||
UserOIDCClaim(oidc.PreferredUsername),
|
||||
UserCS3Claim("username"),
|
||||
AutoprovisionAccounts(false),
|
||||
)(next)
|
||||
|
||||
req, rw := mockRequest(map[string]any{
|
||||
oidc.Iss: testIdP,
|
||||
oidc.PreferredUsername: "foo",
|
||||
})
|
||||
sut.ServeHTTP(rw, req)
|
||||
|
||||
assert.Equal(t, tc.wantStatusCode, rw.Code)
|
||||
assert.False(t, called, "the request must not reach the next handler")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package userroles
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -81,12 +82,13 @@ func (ra oidcRoleAssigner) UpdateUserRoleAssignment(ctx context.Context, user *c
|
||||
|
||||
claimRoles, err := extractRoles(ra.rolesClaim, claims)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%w: %w", ErrRoleClaim, err)
|
||||
logger.Error().Err(err).Msg("Error mapping role names to role ids")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(claimRoles) == 0 {
|
||||
err := errors.New("no roles set in claim")
|
||||
err := fmt.Errorf("%w: no roles set in claim", ErrRoleClaim)
|
||||
logger.Error().Err(err).Msg("")
|
||||
return nil, err
|
||||
}
|
||||
@@ -104,7 +106,7 @@ func (ra oidcRoleAssigner) UpdateUserRoleAssignment(ctx context.Context, user *c
|
||||
}
|
||||
|
||||
if roleIDFromClaim == "" {
|
||||
err := errors.New("no role in claim maps to an OpenCloud role")
|
||||
err := fmt.Errorf("%w: no role in claim maps to an OpenCloud role", ErrRoleClaim)
|
||||
logger.Error().Err(err).Msg("")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package userroles
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/opencloud/services/proxy/pkg/config"
|
||||
)
|
||||
|
||||
func TestExtractRolesArray(t *testing.T) {
|
||||
@@ -118,3 +125,46 @@ func TestNoRoles(t *testing.T) {
|
||||
t.Fatal("length of roles mut be 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserRoleAssignmentClaimErrors(t *testing.T) {
|
||||
// pre-fill the role name cache so no settings service is needed
|
||||
roleNameToID.lock.Lock()
|
||||
roleNameToID.roleNameToID = map[string]string{"admin": "admin-id", "user": "user-id"}
|
||||
roleNameToID.lastRead = time.Now()
|
||||
roleNameToID.lock.Unlock()
|
||||
t.Cleanup(func() {
|
||||
roleNameToID.lock.Lock()
|
||||
roleNameToID.roleNameToID = nil
|
||||
roleNameToID.lastRead = time.Time{}
|
||||
roleNameToID.lock.Unlock()
|
||||
})
|
||||
|
||||
ra := NewOIDCRoleAssigner(
|
||||
WithLogger(log.NewLogger()),
|
||||
WithRolesClaim("organization.roles"),
|
||||
WithRoleMapping([]config.RoleMapping{{RoleName: "admin", ClaimValue: "oc-admin"}}),
|
||||
)
|
||||
user := &cs3.User{Id: &cs3.UserId{OpaqueId: "user-id"}}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
claims map[string]any
|
||||
}{
|
||||
{"claim missing", map[string]any{"sub": "x"}},
|
||||
{"claim is null", map[string]any{"organization": map[string]any{"roles": nil}}},
|
||||
{"claim is empty", map[string]any{"organization": map[string]any{"roles": []any{}}}},
|
||||
{"claim has a non-string role", map[string]any{"organization": map[string]any{"roles": []any{1}}}},
|
||||
{"claim maps to no role", map[string]any{"organization": map[string]any{"roles": []any{"other"}}}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := ra.UpdateUserRoleAssignment(context.Background(), user, tc.claims)
|
||||
if got != nil {
|
||||
t.Fatal("must not return a user")
|
||||
}
|
||||
if !errors.Is(err, ErrRoleClaim) {
|
||||
t.Fatalf("want ErrRoleClaim, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package userroles
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
@@ -11,6 +12,11 @@ import (
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
)
|
||||
|
||||
// ErrRoleClaim is returned (wrapped) by a UserRoleAssigner when the user's claims
|
||||
// do not yield an OpenCloud role: the claim is missing, malformed, empty or maps to no role.
|
||||
// It is a problem with the user's token, not a failure of OpenCloud itself.
|
||||
var ErrRoleClaim = errors.New("role claim does not yield an OpenCloud role")
|
||||
|
||||
// UserRoleAssigner allows providing different implementations for how users get their default roles
|
||||
// assigned by the proxy during authentication
|
||||
type UserRoleAssigner interface {
|
||||
|
||||
@@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-10-14 23:16+0000\n"
|
||||
"POT-Creation-Date: 2026-11-03 23:16+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Frantisek Gössel, 2026\n"
|
||||
"Language-Team: Czech (https://app.transifex.com/opencloud-eu/teams/204053/cs/)\n"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-10-14 23:16+0000\n"
|
||||
"POT-Creation-Date: 2026-11-03 23:16+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: eric_G <junk.eg@free.fr>, 2026\n"
|
||||
"Language-Team: French (https://app.transifex.com/opencloud-eu/teams/204053/fr/)\n"
|
||||
|
||||
Reference in new issue
Block a user