Skip to content

Commit

Permalink
added userretention tests to v28
Browse files Browse the repository at this point in the history
  • Loading branch information
dasarinaidu committed Dec 19, 2024
1 parent 54c7acb commit 41e5dce
Show file tree
Hide file tree
Showing 8 changed files with 1,002 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package lastlogin
package auth

import (
"fmt"
Expand All @@ -13,11 +13,11 @@ import (
)

const (
lastloginLabel = "cattle.io/last-login"
LastloginLabel = "cattle.io/last-login"
)

func getLastLoginTime(labels map[string]string) (lastLogin time.Time, err error) {
value, exists := isLabelPresent(labels)
func GetLastLoginTime(labels map[string]string) (lastLogin time.Time, err error) {
value, exists := IsLabelPresent(labels)

if !exists || value == "" {
return
Expand All @@ -26,20 +26,20 @@ func getLastLoginTime(labels map[string]string) (lastLogin time.Time, err error)
if err != nil {
return
}
lastLogin = convertEpochToTime(epochTime)
lastLogin = ConvertEpochToTime(epochTime)
return
}

func convertEpochToTime(epochTime int64) time.Time {
func ConvertEpochToTime(epochTime int64) time.Time {
return time.Unix(epochTime, 0)
}

func isLabelPresent(labels map[string]string) (string, bool) {
value, exists := labels[lastloginLabel]
func IsLabelPresent(labels map[string]string) (string, bool) {
value, exists := labels[LastloginLabel]
return value, exists
}

func getUserAfterLogin(rancherClient *rancher.Client, user client.User) (userDetails *v3.User, err error) {
func GetUserAfterLogin(rancherClient *rancher.Client, user client.User) (userDetails *v3.User, err error) {

_, err = rancherClient.AsUser(&user)
if err != nil {
Expand Down
64 changes: 64 additions & 0 deletions tests/v2/actions/rbac/rbac.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package rbac

import (
"fmt"
"strings"

"github.com/rancher/norman/types"
"github.com/rancher/shepherd/clients/rancher"
management "github.com/rancher/shepherd/clients/rancher/generated/management/v3"
rbacv2 "github.com/rancher/shepherd/extensions/kubeapi/rbac"
"github.com/rancher/shepherd/extensions/users"
"github.com/sirupsen/logrus"
rbacv1 "k8s.io/api/rbac/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type Role string
Expand Down Expand Up @@ -68,3 +74,61 @@ func SetupUser(client *rancher.Client, globalRole string) (user *management.User
}
return
}

// GetRoleBindings is a helper function to fetch rolebindings for a user
func GetRoleBindings(rancherClient *rancher.Client, clusterID string, userID string) ([]rbacv1.RoleBinding, error) {
logrus.Infof("Getting role bindings for user %s in cluster %s", userID, clusterID)
listOpt := v1.ListOptions{}
roleBindings, err := rbacv2.ListRoleBindings(rancherClient, clusterID, "", listOpt)
if err != nil {
return nil, fmt.Errorf("failed to fetch RoleBindings: %w", err)
}

var userRoleBindings []rbacv1.RoleBinding
for _, rb := range roleBindings.Items {
for _, subject := range rb.Subjects {
if subject.Name == userID {
userRoleBindings = append(userRoleBindings, rb)
break
}
}
}
logrus.Infof("Found %d role bindings for user %s", len(userRoleBindings), userID)
return userRoleBindings, nil
}

// GetBindings is a helper function to fetch bindings for a user
func GetBindings(rancherClient *rancher.Client, userID string) (map[string]interface{}, error) {
logrus.Infof("Getting all bindings for user %s", userID)
bindings := make(map[string]interface{})

roleBindings, err := GetRoleBindings(rancherClient, rbacv2.LocalCluster, userID)
if err != nil {
return nil, fmt.Errorf("failed to get role bindings: %w", err)
}
bindings["RoleBindings"] = roleBindings

logrus.Info("Getting cluster role bindings")
clusterRoleBindings, err := rbacv2.ListClusterRoleBindings(rancherClient, rbacv2.LocalCluster, v1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to list cluster role bindings: %w", err)
}
bindings["ClusterRoleBindings"] = clusterRoleBindings.Items

logrus.Info("Getting global role bindings")
globalRoleBindings, err := rancherClient.Management.GlobalRoleBinding.ListAll(&types.ListOpts{})
if err != nil {
return nil, fmt.Errorf("failed to list global role bindings: %w", err)
}
bindings["GlobalRoleBindings"] = globalRoleBindings.Data

logrus.Info("Getting cluster role template bindings")
clusterRoleTemplateBindings, err := rancherClient.Management.ClusterRoleTemplateBinding.List(&types.ListOpts{})
if err != nil {
return nil, fmt.Errorf("failed to list cluster role template bindings: %w", err)
}
bindings["ClusterRoleTemplateBindings"] = clusterRoleTemplateBindings.Data

logrus.Info("All bindings retrieved successfully")
return bindings, nil
}
57 changes: 29 additions & 28 deletions tests/v2/validation/auth/lastlogin/last_login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"
"time"

"github.com/rancher/rancher/tests/v2/actions/auth"
"github.com/rancher/shepherd/clients/rancher"
users "github.com/rancher/shepherd/extensions/users"
password "github.com/rancher/shepherd/extensions/users/passwordgenerator"
Expand Down Expand Up @@ -45,10 +46,10 @@ func (ll *LastLoginTestSuite) TestLastLoginAdmin() {
adminUser, err := ll.client.Management.User.ByID(adminId)
adminUser.Password = ll.client.RancherConfig.AdminPassword

adminRelogin, err := getUserAfterLogin(ll.client, *adminUser)
adminRelogin, err := auth.GetUserAfterLogin(ll.client, *adminUser)
require.NoError(ll.T(), err)

lastLoginTime, err := getLastLoginTime(adminRelogin.Labels)
lastLoginTime, err := auth.GetLastLoginTime(adminRelogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

Expand All @@ -61,10 +62,10 @@ func (ll *LastLoginTestSuite) TestLastLoginStandardUser() {
require.NoError(ll.T(), err)
ll.T().Logf("Created a standard user: %v", newUser.Username)

userLogin, err := getUserAfterLogin(ll.client, *newUser)
userLogin, err := auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)

lastLoginTime, err := getLastLoginTime(userLogin.Labels)
lastLoginTime, err := auth.GetLastLoginTime(userLogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

Expand All @@ -77,18 +78,18 @@ func (ll *LastLoginTestSuite) TestReLoginUpdatesLastLogin() {
require.NoError(ll.T(), err)
ll.T().Logf("Created a standard user: %v", newUser.Username)

userLogin, err := getUserAfterLogin(ll.client, *newUser)
userLogin, err := auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)
lastLoginTime, err := getLastLoginTime(userLogin.Labels)
lastLoginTime, err := auth.GetLastLoginTime(userLogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

userAttributes, err := ll.client.WranglerContext.Mgmt.UserAttribute().Get(newUser.ID, v1.GetOptions{})
assert.Equal(ll.T(), lastLoginTime, userAttributes.LastLogin.Time)

userLogin, err = getUserAfterLogin(ll.client, *newUser)
userLogin, err = auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)
reLoginLastLoginTime, err := getLastLoginTime(userLogin.Labels)
reLoginLastLoginTime, err := auth.GetLastLoginTime(userLogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

Expand All @@ -108,7 +109,7 @@ func (ll *LastLoginTestSuite) TestUserLastLoginNotUpdated() {

user, err := ll.client.WranglerContext.Mgmt.User().Get(newUser.ID, v1.GetOptions{})

_, exists := user.Labels[lastloginLabel]
_, exists := user.Labels[auth.LastloginLabel]
if exists {
ll.Assert().Fail("Expected the lastlogin to be not present and empty")
}
Expand All @@ -123,19 +124,19 @@ func (ll *LastLoginTestSuite) TestLastLoginNotUpdatedWhenLoginFails() {
require.NoError(ll.T(), err)
ll.T().Logf("Created a standard user: %v", newUser.Username)

userLogin, err := getUserAfterLogin(ll.client, *newUser)
userLogin, err := auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)
lastLoginTimePreFailure, err := getLastLoginTime(userLogin.Labels)
lastLoginTimePreFailure, err := auth.GetLastLoginTime(userLogin.Labels)
require.NoError(ll.T(), err)

newUser.Password = password.GenerateUserPassword("testpass-")
_, err = getUserAfterLogin(ll.client, *newUser)
_, err = auth.GetUserAfterLogin(ll.client, *newUser)
require.Error(ll.T(), err)
k8sErrors.IsUnauthorized(err)

userDetails, err := ll.client.WranglerContext.Mgmt.User().Get(newUser.ID, v1.GetOptions{})
require.NoError(ll.T(), err)
lastLoginTimePostFailure, err := getLastLoginTime(userDetails.Labels)
lastLoginTimePostFailure, err := auth.GetLastLoginTime(userDetails.Labels)
require.NoError(ll.T(), err)
userAttributes, err := ll.client.WranglerContext.Mgmt.UserAttribute().Get(newUser.ID, v1.GetOptions{})
require.NoError(ll.T(), err)
Expand All @@ -150,16 +151,16 @@ func (ll *LastLoginTestSuite) TestStandardUserCantUpdateLastLogin() {
require.NoError(ll.T(), err)
ll.T().Logf("Created a standard user: %v", newUser.Username)

updatedUserLogin, err := getUserAfterLogin(ll.client, *newUser)
updatedUserLogin, err := auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)

lastLoginTime, err := getLastLoginTime(updatedUserLogin.Labels)
lastLoginTime, err := auth.GetLastLoginTime(updatedUserLogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

lastLoginTime = lastLoginTime.Add(48 * time.Hour)
newLastLogin := strconv.FormatInt(lastLoginTime.Unix(), 10)
updatedUserLogin.Labels[lastloginLabel] = newLastLogin
updatedUserLogin.Labels[auth.LastloginLabel] = newLastLogin

standardClient, err := ll.client.AsUser(newUser)
require.NoError(ll.T(), err)
Expand All @@ -176,14 +177,14 @@ func (ll *LastLoginTestSuite) TestStandardUserCantDeleteLastLogin() {
require.NoError(ll.T(), err)
ll.T().Logf("Created a standard user: %v", newUser.Username)

updatedUserLogin, err := getUserAfterLogin(ll.client, *newUser)
updatedUserLogin, err := auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)

_, exists := updatedUserLogin.Labels[lastloginLabel]
_, exists := updatedUserLogin.Labels[auth.LastloginLabel]
if !exists {
ll.Assert().Fail("Expected the lastlogin to be not present and empty")
}
delete(updatedUserLogin.Labels, lastloginLabel)
delete(updatedUserLogin.Labels, auth.LastloginLabel)

standardClient, err := ll.client.AsUser(newUser)
require.NoError(ll.T(), err)
Expand All @@ -199,14 +200,14 @@ func (ll *LastLoginTestSuite) TestAdminCanDeleteLastLogin() {
require.NoError(ll.T(), err)
ll.T().Logf("Created a standard user: %v", newUser.Username)

updatedUserLogin, err := getUserAfterLogin(ll.client, *newUser)
updatedUserLogin, err := auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)

_, exists := updatedUserLogin.Labels[lastloginLabel]
_, exists := updatedUserLogin.Labels[auth.LastloginLabel]
if !exists {
ll.Assert().Fail("Expected the lastlogin to be not present and empty")
}
delete(updatedUserLogin.Labels, lastloginLabel)
delete(updatedUserLogin.Labels, auth.LastloginLabel)

_, err = ll.client.WranglerContext.Mgmt.User().Update(updatedUserLogin)
require.NoError(ll.T(), err)
Expand All @@ -223,21 +224,21 @@ func (ll *LastLoginTestSuite) TestConcurrentLoginAsMultipleUsers() {
ll.T().Logf("Created a standard user: %v", newUser2.Username)

go func() {
userLogin, err := getUserAfterLogin(ll.client, *newUser1)
userLogin, err := auth.GetUserAfterLogin(ll.client, *newUser1)
require.NoError(ll.T(), err)

lastLoginTime, err := getLastLoginTime(userLogin.Labels)
lastLoginTime, err := auth.GetLastLoginTime(userLogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

userAttributes, err := ll.client.WranglerContext.Mgmt.UserAttribute().Get(newUser1.ID, v1.GetOptions{})
assert.Equal(ll.T(), lastLoginTime, userAttributes.LastLogin.Time)
}()
go func() {
userLogin, err := getUserAfterLogin(ll.client, *newUser2)
userLogin, err := auth.GetUserAfterLogin(ll.client, *newUser2)
require.NoError(ll.T(), err)

lastLoginTime, err := getLastLoginTime(userLogin.Labels)
lastLoginTime, err := auth.GetLastLoginTime(userLogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

Expand All @@ -253,10 +254,10 @@ func (ll *LastLoginTestSuite) TestConcurrentRefreshAndLogin() {
ll.T().Logf("Created a standard user: %v", newUser.Username)

go func() {
userLogin, err := getUserAfterLogin(ll.client, *newUser)
userLogin, err := auth.GetUserAfterLogin(ll.client, *newUser)
require.NoError(ll.T(), err)

lastLoginTime, err := getLastLoginTime(userLogin.Labels)
lastLoginTime, err := auth.GetLastLoginTime(userLogin.Labels)
require.NoError(ll.T(), err)
require.False(ll.T(), lastLoginTime.IsZero(), "Last login is empty")

Expand Down
28 changes: 28 additions & 0 deletions tests/v2/validation/auth/userretention/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Userretention

The User Retention feature in Rancher allows administrators to manage inactive users by automatically disabling and deleting them based on predefined settings and a user retention cron job. This functionality is implemented based on the user's last login timestamp. The tests included in this package focus on validating the behavior of the User Retention feature, including updating retention settings and verifying the proper disabling and deletion of inactive users.

## Pre-requisites

- The tests are designed to be executed on a local cluster. They can be run on a Rancher server without any downstream clusters.
- Update the default admin password in the configuration file. The tests rely on the admin password to authenticate and perform actions as the default admin user in Rancher.

## Getting Started

Your GO suite should be set to `-run ^Test<>TestSuite$`.

To run the userretention_test.go, set the GO suite to `-run ^TestUserRetentionSettingsSuite$` You can find specific tests by checking the test file you plan to run.
To run the userretention_disable_user_test.go, set the GO suite to `-run ^TestURDisableUserSuite$` You can find specific tests by checking the test file you plan to run.
To run the userretention_delete_user_test.go, set the GO suite to `-run ^TestURDeleteUserSuite$` You can find specific tests by checking the test file you plan to run.

In your config file, set the following:

```json
"rancher": {
"host": "rancher_server_address",
"adminToken": "rancher_admin_token",
"insecure": true/optional,
"cleanup": false/optional,
"adminPassword": "<adminPassword>"
}
```
Loading

0 comments on commit 41e5dce

Please sign in to comment.