mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Alias JIT Saml "team" attribute to FLEET_JIT_USER_ROLE_FLEET_<FLEET ID> (#41402)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #40642 # Details This PR adds `FLEET_JIT_USER_ROLE_FLEET_` as an expected Saml attribute alongside `FLEET_JIT_USER_ROLE_TEAM_`. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [X] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [X] Added/updated automated tests - [X] QA'd all new/changed functionality manually Tested with SimpleSAML SSO. - [x] Updated `users.php` to use both the new attribute and the old attribute for a user, and was able to log in with that user and see them created using JIT with the correct permissions
This commit is contained in:
parent
9424db4858
commit
2d4e72ac7a
5 changed files with 150 additions and 13 deletions
1
changes/40642-add-new-fleet-saml-attribute-prefix
Normal file
1
changes/40642-add-new-fleet-saml-attribute-prefix
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Added ability to use `FLEET_JIT_USER_ROLE_FLEET_` as a prefix on SAML attributes
|
||||
|
|
@ -111,15 +111,16 @@ func (s SSORolesInfo) IsSet() bool {
|
|||
}
|
||||
|
||||
const (
|
||||
globalUserRoleSSOAttrName = "FLEET_JIT_USER_ROLE_GLOBAL"
|
||||
teamUserRoleSSOAttrNamePrefix = "FLEET_JIT_USER_ROLE_TEAM_"
|
||||
ssoAttrNullRoleValue = "null"
|
||||
globalUserRoleSSOAttrName = "FLEET_JIT_USER_ROLE_GLOBAL"
|
||||
teamUserRoleSSOAttrNamePrefix = "FLEET_JIT_USER_ROLE_TEAM_"
|
||||
teamUserRoleSSOAttrNamePrefixV2 = "FLEET_JIT_USER_ROLE_FLEET_"
|
||||
ssoAttrNullRoleValue = "null"
|
||||
)
|
||||
|
||||
// RolesFromSSOAttributes loads Global and Team roles from SAML custom attributes.
|
||||
// - Custom attribute `FLEET_JIT_USER_ROLE_GLOBAL` is used for setting global role.
|
||||
// - Custom attributes of the form `FLEET_JIT_USER_ROLE_TEAM_<TEAM_ID>` are used
|
||||
// for setting role for a team with ID <TEAM_ID>.
|
||||
// - Custom attributes of the form `FLEET_JIT_USER_ROLE_TEAM_<FLEET_ID>` or
|
||||
// `FLEET_JIT_USER_ROLE_FLEET_<FLEET_ID>` are used for setting role for a fleet with ID <FLEET_ID>.
|
||||
//
|
||||
// For both attributes currently supported values are `admin`, `maintainer`, `observer`,
|
||||
// `observer_plus`, `technician` and `null`. A `null` value is used to ignore the attribute.
|
||||
|
|
@ -137,8 +138,12 @@ func RolesFromSSOAttributes(attributes []SAMLAttribute) (SSORolesInfo, error) {
|
|||
continue
|
||||
}
|
||||
ssoRolesInfo.Global = ptr.String(role)
|
||||
case strings.HasPrefix(attribute.Name, teamUserRoleSSOAttrNamePrefix):
|
||||
case strings.HasPrefix(attribute.Name, teamUserRoleSSOAttrNamePrefix),
|
||||
strings.HasPrefix(attribute.Name, teamUserRoleSSOAttrNamePrefixV2):
|
||||
// Get rid of any prefix, v1 or v2.
|
||||
teamIDSuffix := strings.TrimPrefix(attribute.Name, teamUserRoleSSOAttrNamePrefix)
|
||||
teamIDSuffix = strings.TrimPrefix(teamIDSuffix, teamUserRoleSSOAttrNamePrefixV2)
|
||||
// Parse the fleet ID from what's left.
|
||||
teamID, err := strconv.ParseUint(teamIDSuffix, 10, 32)
|
||||
if err != nil {
|
||||
return SSORolesInfo{}, fmt.Errorf("parse team ID: %w", err)
|
||||
|
|
|
|||
|
|
@ -349,6 +349,133 @@ func TestRolesFromSSOAttributes(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v2-prefix-all-teams",
|
||||
attributes: []SAMLAttribute{
|
||||
{
|
||||
Name: "FLEET_JIT_USER_ROLE_FLEET_1",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "observer"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "FLEET_JIT_USER_ROLE_FLEET_2",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldFail: false,
|
||||
expectedSSORolesInfo: SSORolesInfo{
|
||||
Global: nil,
|
||||
Teams: []TeamRole{
|
||||
{
|
||||
ID: 1,
|
||||
Role: "observer",
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Role: "admin",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v2-prefix-global-and-team",
|
||||
attributes: []SAMLAttribute{
|
||||
{
|
||||
Name: globalUserRoleSSOAttrName,
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "admin"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "FLEET_JIT_USER_ROLE_FLEET_5",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "observer"},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldFail: true,
|
||||
expectedSSORolesInfo: SSORolesInfo{},
|
||||
},
|
||||
{
|
||||
name: "v2-prefix-invalid-team-id",
|
||||
attributes: []SAMLAttribute{
|
||||
{
|
||||
Name: "FLEET_JIT_USER_ROLE_FLEET_foo",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "observer"},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldFail: true,
|
||||
expectedSSORolesInfo: SSORolesInfo{},
|
||||
},
|
||||
{
|
||||
name: "v2-prefix-null-value-ignored",
|
||||
attributes: []SAMLAttribute{
|
||||
{
|
||||
Name: "FLEET_JIT_USER_ROLE_FLEET_1",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "null"},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldFail: false,
|
||||
expectedSSORolesInfo: SSORolesInfo{},
|
||||
},
|
||||
{
|
||||
name: "v2-prefix-team-technician",
|
||||
attributes: []SAMLAttribute{
|
||||
{
|
||||
Name: "FLEET_JIT_USER_ROLE_FLEET_3",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "technician"},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldFail: false,
|
||||
expectedSSORolesInfo: SSORolesInfo{
|
||||
Teams: []TeamRole{
|
||||
{
|
||||
ID: 3,
|
||||
Role: "technician",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mixed-v1-and-v2-prefixes",
|
||||
attributes: []SAMLAttribute{
|
||||
{
|
||||
Name: teamUserRoleSSOAttrNamePrefix + "1",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "observer"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "FLEET_JIT_USER_ROLE_FLEET_2",
|
||||
Values: []SAMLAttributeValue{
|
||||
{Value: "admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldFail: false,
|
||||
expectedSSORolesInfo: SSORolesInfo{
|
||||
Global: nil,
|
||||
Teams: []TeamRole{
|
||||
{
|
||||
ID: 1,
|
||||
Role: "observer",
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Role: "admin",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global-gitops-not-supported-for-jit",
|
||||
attributes: []SAMLAttribute{
|
||||
|
|
|
|||
|
|
@ -4865,7 +4865,7 @@ func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() {
|
|||
// auto-incremented and other tests cause it to be different than what we need (ID=1).
|
||||
var execErr error
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(db sqlx.ExtContext) error {
|
||||
_, execErr = db.ExecContext(context.Background(), `INSERT INTO teams (id, name) VALUES (1, 'Foobar') ON DUPLICATE KEY UPDATE name = VALUES(name);`)
|
||||
_, execErr = db.ExecContext(context.Background(), `INSERT INTO teams (id, name) VALUES (1, 'Foobar'), (2, 'Hollatchaboi') ON DUPLICATE KEY UPDATE name = VALUES(name);`)
|
||||
return execErr
|
||||
})
|
||||
require.NoError(t, execErr)
|
||||
|
|
@ -4886,9 +4886,11 @@ func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() {
|
|||
require.Equal(t, "sso_user_4_team_maintainer@example.com", user4.Email)
|
||||
require.Equal(t, "SSO User 4", user4.Name)
|
||||
require.Nil(t, user4.GlobalRole)
|
||||
require.Len(t, user4.Teams, 1)
|
||||
require.Len(t, user4.Teams, 2)
|
||||
require.Equal(t, uint(1), user4.Teams[0].ID)
|
||||
require.Equal(t, fleet.RoleMaintainer, user4.Teams[0].Role)
|
||||
require.Equal(t, uint(2), user4.Teams[1].ID)
|
||||
require.Equal(t, fleet.RoleObserver, user4.Teams[1].Role)
|
||||
|
||||
// A user with pre-configured roles can be created,
|
||||
// see `tools/saml/users.php` for details.
|
||||
|
|
|
|||
|
|
@ -30,18 +30,20 @@ $config = array(
|
|||
'email' => 'sso_user_3_global_admin@example.com',
|
||||
'FLEET_JIT_USER_ROLE_GLOBAL' => 'admin',
|
||||
),
|
||||
// sso_user_4_team_maintainer has FLEET_JIT_USER_ROLE_TEAM_1 attribute to be added as maintainer
|
||||
// sso_user_4_team_maintainer has FLEET_JIT_USER_ROLE_FLEET_1 attribute to be added as maintainer
|
||||
// of team with ID 1, its login will fail if team with ID 1 doesn't exist.
|
||||
// It also uses the *older* attribute name to add the user to fleet #2 as an observer.
|
||||
'sso_user_4_team_maintainer:user123#' => array(
|
||||
'uid' => array('4'),
|
||||
'eduPersonAffiliation' => array('group1'),
|
||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name' => array('SSO User 4'),
|
||||
'email' => 'sso_user_4_team_maintainer@example.com',
|
||||
'FLEET_JIT_USER_ROLE_TEAM_1' => 'maintainer',
|
||||
'FLEET_JIT_USER_ROLE_FLEET_1' => 'maintainer',
|
||||
'FLEET_JIT_USER_ROLE_TEAM_2' => 'observer',
|
||||
),
|
||||
// sso_user_5_team_admin has FLEET_JIT_USER_ROLE_TEAM_1 attribute to be added as admin
|
||||
// of team with ID 1, its login will fail if team with ID 1 doesn't exist.
|
||||
// It also sets FLEET_JIT_USER_ROLE_GLOBAL and FLEET_JIT_USER_ROLE_TEAM_2 to `null` which means
|
||||
// It also sets FLEET_JIT_USER_ROLE_GLOBAL and FLEET_JIT_USER_ROLE_FLEET_2 to `null` which means
|
||||
// Fleet will ignore such fields.
|
||||
'sso_user_5_team_admin:user123#' => array(
|
||||
'uid' => array('5'),
|
||||
|
|
@ -50,7 +52,7 @@ $config = array(
|
|||
'email' => 'sso_user_5_team_admin@example.com',
|
||||
'FLEET_JIT_USER_ROLE_TEAM_1' => 'admin',
|
||||
'FLEET_JIT_USER_ROLE_GLOBAL' => 'null',
|
||||
'FLEET_JIT_USER_ROLE_TEAM_2' => 'null',
|
||||
'FLEET_JIT_USER_ROLE_FLEET_2' => 'null',
|
||||
),
|
||||
// sso_user_6_global_observer has all FLEET_JIT_USER_ROLE_* attributes set to null, so it
|
||||
// will be added as global observer (default).
|
||||
|
|
@ -60,7 +62,7 @@ $config = array(
|
|||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name' => array('SSO User 6'),
|
||||
'email' => 'sso_user_6_global_observer@example.com',
|
||||
'FLEET_JIT_USER_ROLE_GLOBAL' => 'null',
|
||||
'FLEET_JIT_USER_ROLE_TEAM_1' => 'null',
|
||||
'FLEET_JIT_USER_ROLE_FLEET_1' => 'null',
|
||||
),
|
||||
// sso_user_no_displayname does not have a displayName/fullName
|
||||
'sso_user_no_displayname:user123#' => array(
|
||||
|
|
|
|||
Loading…
Reference in a new issue