Merge branch '2021.9.x-feature-idp-roles' into 2021.9.x
This commit is contained in:
@@ -2,9 +2,12 @@ package oauth2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/auth/request"
|
||||
"github.com/go-oauth2/oauth2/v4/server"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/auth/request"
|
||||
internalAuth "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/go-oauth2/oauth2/v4/server"
|
||||
)
|
||||
|
||||
func NewUserAuthorizer(sm *request.SessionManager, loginURL, clientAuthURL string) server.UserAuthorizationHandler {
|
||||
@@ -40,7 +43,12 @@ func NewUserAuthorizer(sm *request.SessionManager, loginURL, clientAuthURL strin
|
||||
var roles = request.GetRoleMemberships(ses)
|
||||
if client.Security != nil {
|
||||
// filter user's roles with client security settings
|
||||
roles = client.Security.ProcessRoles(roles...)
|
||||
roles = internalAuth.ApplyRoleSecurity(
|
||||
payload.ParseUint64s(client.Security.PermittedRoles),
|
||||
payload.ParseUint64s(client.Security.ProhibitedRoles),
|
||||
payload.ParseUint64s(client.Security.ForcedRoles),
|
||||
roles...,
|
||||
)
|
||||
}
|
||||
|
||||
// User authenticated, client authorized!
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/slice"
|
||||
)
|
||||
|
||||
// ApplyRoleSecurity takes role security params (set of permitted, prohibited and forced roles)
|
||||
// and applies these rules to the set of given roles
|
||||
//
|
||||
// Filtered set of roles is returned
|
||||
//
|
||||
// String slices are used intentionally, because of the data source used
|
||||
func ApplyRoleSecurity(permitted, prohibited, forced []uint64, rr ...uint64) (out []uint64) {
|
||||
var (
|
||||
mPermitted = slice.ToUint64BoolMap(permitted)
|
||||
mProhibited = slice.ToUint64BoolMap(prohibited)
|
||||
mForced = slice.ToUint64BoolMap(forced)
|
||||
)
|
||||
|
||||
// iterate over user's roles and just append them (obeying allow&deny rules)
|
||||
// to list of mForced roles
|
||||
for _, r := range rr {
|
||||
if (len(mPermitted) == 0 || mPermitted[r]) && !mProhibited[r] {
|
||||
mForced[r] = true
|
||||
}
|
||||
}
|
||||
|
||||
out = make([]uint64, 0, len(mForced))
|
||||
for forcedRoleID := range mForced {
|
||||
out = append(out, forcedRoleID)
|
||||
}
|
||||
|
||||
// for stable output
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
return out[i] < out[j]
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestApplyRoleSecurity(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
permitted []uint64
|
||||
prohibited []uint64
|
||||
forced []uint64
|
||||
roles []uint64
|
||||
wantOut []uint64
|
||||
}{
|
||||
{
|
||||
"empty",
|
||||
[]uint64{},
|
||||
[]uint64{},
|
||||
[]uint64{},
|
||||
[]uint64{},
|
||||
[]uint64{},
|
||||
},
|
||||
{
|
||||
"nil",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
[]uint64{},
|
||||
},
|
||||
{
|
||||
"one",
|
||||
[]uint64{1},
|
||||
[]uint64{2},
|
||||
[]uint64{3},
|
||||
[]uint64{1, 2},
|
||||
[]uint64{1, 3},
|
||||
},
|
||||
{
|
||||
"forced only",
|
||||
[]uint64{},
|
||||
[]uint64{},
|
||||
[]uint64{3, 2, 1},
|
||||
[]uint64{2},
|
||||
[]uint64{1, 2, 3},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.wantOut, ApplyRoleSecurity(tt.permitted, tt.prohibited, tt.forced, tt.roles...))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/eventbus"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rand"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/service/event"
|
||||
@@ -962,6 +963,39 @@ func (svc auth) procLogin(ctx context.Context, s store.Storer, u *types.User, c
|
||||
return
|
||||
}
|
||||
|
||||
{
|
||||
var eapSec *types.ExternalAuthProviderSecurity
|
||||
|
||||
switch p.Provider {
|
||||
case credentialsTypePassword:
|
||||
// nothing to do with password provider
|
||||
|
||||
case "saml":
|
||||
// we need to fetch SAML security settings from different part of settings
|
||||
eapSec = &CurrentSettings.Auth.External.Saml.Security
|
||||
|
||||
default:
|
||||
eap := CurrentSettings.Auth.External.Providers.FindByHandle(p.Provider)
|
||||
|
||||
if eap != nil {
|
||||
eapSec = &eap.Security
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if eapSec != nil {
|
||||
// if authenticated with external auth provider
|
||||
// there might be additional roles that need to be
|
||||
// set to this security session
|
||||
u.SetRoles(internalAuth.ApplyRoleSecurity(
|
||||
payload.ParseUint64s(eapSec.PermittedRoles),
|
||||
payload.ParseUint64s(eapSec.ProhibitedRoles),
|
||||
payload.ParseUint64s(eapSec.ForcedRoles),
|
||||
u.Roles()...,
|
||||
)...)
|
||||
}
|
||||
}
|
||||
|
||||
if c != nil {
|
||||
c.LastUsedAt = now()
|
||||
if err = store.UpdateCredentials(ctx, s, c); err != nil {
|
||||
|
||||
@@ -232,6 +232,10 @@ func (svc *settings) BulkSet(ctx context.Context, vv types.SettingValueSet) (err
|
||||
return
|
||||
}
|
||||
|
||||
if err = store.DeleteSetting(ctx, svc.store, vv.Trash()...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
svc.logChange(ctx, vv)
|
||||
|
||||
return svc.updateCurrent(ctx, vv)
|
||||
|
||||
@@ -110,6 +110,8 @@ type (
|
||||
IdentHandle string `kv:"ident-handle"`
|
||||
IdentIdentifier string `kv:"ident-identifier"`
|
||||
} `kv:"idp"`
|
||||
|
||||
Security ExternalAuthProviderSecurity `json:"-" kv:"security,final"`
|
||||
}
|
||||
|
||||
// all external providers we know
|
||||
@@ -223,6 +225,38 @@ type (
|
||||
RedirectUrl string `json:"-" kv:"redirect"`
|
||||
IssuerUrl string `json:"-" kv:"issuer"`
|
||||
Weight int `json:"-"`
|
||||
|
||||
Security ExternalAuthProviderSecurity `json:"-" kv:"security,final"`
|
||||
}
|
||||
|
||||
ExternalAuthProviderSecurity struct {
|
||||
// Subset of roles, permitted to be used with this client
|
||||
// when authorizing via this auth provider.
|
||||
//
|
||||
// IDs are intentionally stored as strings to support JS (int64 only)
|
||||
//
|
||||
PermittedRoles []string `json:"permittedRoles,omitempty"`
|
||||
|
||||
// Subset of roles, prohibited to be used with this client
|
||||
// when authorizing via this auth provider.
|
||||
//
|
||||
// IDs are intentionally stored as strings to support JS (int64 only)
|
||||
//
|
||||
ProhibitedRoles []string `json:"prohibitedRoles,omitempty"`
|
||||
|
||||
// Set of additional roles that are forced on this user
|
||||
// when authorizing via this auth provider.
|
||||
//
|
||||
// IDs are intentionally stored as strings to support JS (int64 only)
|
||||
ForcedRoles []string `json:"forcedRoles,omitempty"`
|
||||
|
||||
// Map external roles or groups to internal
|
||||
//
|
||||
// If IdP provides a list of roles (groups) along side authenticated user
|
||||
// these roles can be mapped to the valid local roles
|
||||
//
|
||||
// @todo implement mapped roles
|
||||
// MappedRoles map[string]string `json:"mappedRoles,omitempty"`
|
||||
}
|
||||
|
||||
SmtpServers struct {
|
||||
|
||||
@@ -4,11 +4,9 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/slice"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -221,32 +219,3 @@ func (vv *AuthClientSecurity) Value() (driver.Value, error) {
|
||||
|
||||
return json.Marshal(vv)
|
||||
}
|
||||
|
||||
// Takes user's roles, filter out only allowed roles (when set), remove denied and add all forced
|
||||
func (vv *AuthClientSecurity) ProcessRoles(rr ...uint64) (out []uint64) {
|
||||
var (
|
||||
permitted = slice.ToStringBoolMap(vv.PermittedRoles)
|
||||
prohibited = slice.ToStringBoolMap(vv.ProhibitedRoles)
|
||||
forced = slice.ToStringBoolMap(vv.ForcedRoles)
|
||||
aux string
|
||||
roleID uint64
|
||||
)
|
||||
|
||||
// iterate over user's roles and just append them (obeying allow&deny rules)
|
||||
// to list of forced roles
|
||||
for _, r := range rr {
|
||||
aux = strconv.FormatUint(r, 10)
|
||||
if (len(vv.PermittedRoles) == 0 || permitted[aux]) && !prohibited[aux] {
|
||||
forced[aux] = true
|
||||
}
|
||||
}
|
||||
|
||||
out = make([]uint64, 0, len(forced))
|
||||
for i := range forced {
|
||||
if roleID, _ = strconv.ParseUint(i, 10, 64); roleID > 0 {
|
||||
out = append(out, roleID)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -88,6 +88,10 @@ func (v *SettingValue) Bool() (out bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (v *SettingValue) IsNull() (out bool) {
|
||||
return v == nil || string(v.Value) == "null" || string(v.Value) == ""
|
||||
}
|
||||
|
||||
func (v *SettingValue) NormalizeValue() {
|
||||
|
||||
}
|
||||
@@ -232,7 +236,7 @@ func (set SettingValueSet) New(in SettingValueSet) (out SettingValueSet) {
|
||||
org := set.KV()
|
||||
|
||||
for _, v := range in {
|
||||
if !org.Has(v.Name) {
|
||||
if !org.Has(v.Name) && !v.IsNull() {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
@@ -240,12 +244,23 @@ func (set SettingValueSet) New(in SettingValueSet) (out SettingValueSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// New returns all new values (that do not exist in the original set)
|
||||
// New returns all old values (that exist in the original set)
|
||||
func (set SettingValueSet) Old(in SettingValueSet) (out SettingValueSet) {
|
||||
org := set.KV()
|
||||
|
||||
for _, v := range in {
|
||||
if org.Has(v.Name) {
|
||||
if org.Has(v.Name) && !v.IsNull() {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Trash returns values from the set that were set ti nil
|
||||
func (set SettingValueSet) Trash() (out SettingValueSet) {
|
||||
for _, v := range set {
|
||||
if v.IsNull() {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user