diff --git a/lib/auth/session_access.go b/lib/auth/session_access.go index 28a6df0a763..bd7e6e0cd98 100644 --- a/lib/auth/session_access.go +++ b/lib/auth/session_access.go @@ -162,7 +162,7 @@ func (e *SessionAccessEvaluator) matchesJoin(allow *types.SessionJoinPolicy) boo for _, allowRole := range allow.Roles { // GlobToRegexp makes sure this is always a valid regexp. - expr := regexp.MustCompile(utils.GlobToRegexp(allowRole)) + expr := regexp.MustCompile("^" + utils.GlobToRegexp(allowRole) + "$") for _, policySet := range e.policySets { if expr.MatchString(policySet.Name) { diff --git a/lib/auth/session_access_test.go b/lib/auth/session_access_test.go index 1932c189b6f..85105273814 100644 --- a/lib/auth/session_access_test.go +++ b/lib/auth/session_access_test.go @@ -459,6 +459,35 @@ func failKindJoinTestCase(t *testing.T) joinTestCase { } } +// Tests to make sure that the regexp matching for roles only matches a full string +// match and not just any substring match. +// In this test case, we are making sure that having access to sessions hosted +// by someone with the role `test` doesn't also grant you access to sessions +// hosted by someone with the role `prod-test`. +func failJoinRoleNameInSubstringTestCase(t *testing.T) joinTestCase { + hostRole, err := types.NewRole("prod-test", types.RoleSpecV6{}) + require.NoError(t, err) + participantRole, err := types.NewRole("participant", types.RoleSpecV6{}) + require.NoError(t, err) + + participantRole.SetSessionJoinPolicies([]*types.SessionJoinPolicy{{ + Roles: []string{"test"}, + Kinds: []string{string(types.SSHSessionKind), string(types.KubernetesSessionKind)}, + Modes: []string{types.Wildcard}, + }}) + + return joinTestCase{ + name: "failRoleInSubstring", + host: hostRole, + sessionKinds: []types.SessionKind{types.SSHSessionKind, types.KubernetesSessionKind}, + participant: SessionAccessContext{ + Username: "participant", + Roles: []types.Role{participantRole}, + }, + expected: []bool{false, false}, + } +} + func versionDefaultJoinTestCase(t *testing.T) joinTestCase { hostRole, err := types.NewRole("host", types.RoleSpecV6{}) require.NoError(t, err) @@ -495,6 +524,7 @@ func TestSessionAccessJoin(t *testing.T) { successSameUserJoinTestCase(t), failRoleJoinTestCase(t), failKindJoinTestCase(t), + failJoinRoleNameInSubstringTestCase(t), versionDefaultJoinTestCase(t), } diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 0780827738a..3dcdb0d26fd 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -2862,12 +2862,6 @@ func (h *Handler) siteSessionsGet(w http.ResponseWriter, r *http.Request, p http return nil, trace.Wrap(err) } - var policySets []*types.SessionTrackerPolicySet - for _, role := range userRoles { - policySet := role.GetSessionPolicySet() - policySets = append(policySets, &policySet) - } - accessContext := auth.SessionAccessContext{ Username: sctx.GetUser(), Roles: userRoles, @@ -2878,7 +2872,7 @@ func (h *Handler) siteSessionsGet(w http.ResponseWriter, r *http.Request, p http if tracker.GetState() != types.SessionState_SessionStateTerminated { session := trackerToLegacySession(tracker, p.ByName("site")) // Get the participant modes available to the user from their roles. - accessEvaluator := auth.NewSessionAccessEvaluator(policySets, types.SSHSessionKind, session.Owner) + accessEvaluator := auth.NewSessionAccessEvaluator(tracker.GetHostPolicySets(), types.SSHSessionKind, tracker.GetHostUser()) participantModes := accessEvaluator.CanJoin(accessContext) sessions = append(sessions, siteSessionsGetResponseSession{Session: session, ParticipantModes: participantModes}) diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 5bb610bd83d..bcdba808962 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -1924,6 +1924,9 @@ func TestActiveSessions(t *testing.T) { s := newWebSuite(t) pack := s.authPack(t, "foo") + // Use enterprise license (required for moderated sessions). + modules.SetTestModules(t, &modules.TestModules{TestBuildType: modules.BuildEnterprise}) + start := time.Now() kinds := []types.SessionKind{ types.SSHSessionKind, @@ -1951,6 +1954,17 @@ func TestActiveSessions(t *testing.T) { Participants: []types.Participant{ {ID: "id", User: "user-1", LastActive: start}, }, + HostPolicies: []*types.SessionTrackerPolicySet{ + { + Name: "foo", + Version: "5", + RequireSessionJoin: []*types.SessionRequirePolicy{ + { + Name: "foo", + }, + }, + }, + }, }) require.NoError(t, err) ids[tracker.GetSessionID()] = struct{}{}