github.com/tommi2day/pwcli@v0.0.0-20240317203041-4d1177a5ab91/cmd/ldap_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/go-ldap/ldap/v3"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/tommi2day/gomodules/common"
    13  	"github.com/tommi2day/gomodules/ldaplib"
    14  	"github.com/tommi2day/pwcli/test"
    15  )
    16  
    17  const LdapBaseDn = "dc=example,dc=local"
    18  const LdapAdminUser = "cn=admin," + LdapBaseDn
    19  const LdapAdminPassword = "admin"
    20  const LdapConfigPassword = "config"
    21  const LdapTestUserDN = "cn=test,ou=Users," + LdapBaseDn
    22  const LdapTestUserPassword = "test"
    23  const LdapTestUser2DN = "cn=test2,ou=Users," + LdapBaseDn
    24  const LdapTestUser2Password = "test2"
    25  const LdapNewPassword = "newpassword"
    26  
    27  var sshkey = `ssh-rsa AAAAB3NzaC1yc2EAABBDAQABAFFBAQDX9uIBEySOR6tASa4RIgUo6TTOi+o3hIWkxJGlfajHQY9f73LONotAPKgDfEdvrvY+0
    28  pW3zXe3pmr4GhQzP2c1EMYmwVdkkQLtn/FHFICLHCyihN2byMe14v4iv1em6XXLqVB7cbxi2XKHHfa50tqgeEJTIRVbFVht9WCdHQ9VUvwnCUda6wDt3E1q+
    29  tAaUOldrfFl3KR4LQThOUOEOtaG1eU2Q/fk1j5qLMH2sDtzYnTp2MgLVAElC7XH9QDWz4+I3uxeYOweUhvBnBx+Ti2ZkzZjchRbkawa4v/woySmWove7nzp
    30  BPYWJ8mBdRecVfcY+/jZDSe2Phgfzgf3cRTvs3tF test@ldap`
    31  var sshkey2 = `ssh-rsa AAAAB3NzaC1yc2EAABBDAQABAFFBAQDX9uIBEySOR6tASa4RIgUo6TTOi+o3hIWkxJGlfajHQY9f73LONotAPKgDfEdvrvY+0
    32  pW3zXe3pmr4GhQzP2c1EMYmwVdkkQLtn/FHFICLHCyihN2byMe14v4iv1em6XXLqVB7cbxi2XKHHfa50tqgeEJTIRVbFVht9WCdHQ9VUvwnCUda6wDt3E1q+
    33  tAaUOldrfFl3KR4LQThOUOEOtaG1eU2Q/fk1j5qLMH2sDtzYnTp2MgLVAElC7XH9QDWz4+I3uxeYOweUhvBnBx+Ti2ZkzZjchRbkawa4v/woySmWove7nzp
    34  BPYWJ8mBdRecVfcY+/jZDSe2Phgfzgf3cRTvs3tF test2@ldap`
    35  var lc *ldaplib.LdapConfigType
    36  
    37  func TestLdap(t *testing.T) {
    38  	var err error
    39  	var server string
    40  	var sslport int
    41  	var out string
    42  
    43  	test.Testinit(t)
    44  	err = os.Chdir(test.TestDir)
    45  	require.NoErrorf(t, err, "ChDir failed")
    46  	ldapAdmin := test.TestData
    47  	sshkeyfile := ldapAdmin + "/id_rsa.pub"
    48  	sshkeyfile2 := ldapAdmin + "/id_rsa2.pub"
    49  	//nolint gosec
    50  	err = os.WriteFile(sshkeyfile, []byte(sshkey), 0644)
    51  	require.NoErrorf(t, err, "Create test id_rsa.pub failed")
    52  	//nolint gosec
    53  	err = os.WriteFile(sshkeyfile2, []byte(sshkey2), 0644)
    54  	require.NoErrorf(t, err, "Create test id_rsa2.pub failed")
    55  
    56  	// redirect Stdin for test
    57  	r, w, err := os.Pipe()
    58  	require.NoErrorf(t, err, "Pipe failed")
    59  	inputReader = r
    60  
    61  	// prepare or skip container based tests
    62  	if os.Getenv("SKIP_LDAP") != "" {
    63  		t.Skip("Skipping LDAP testing in CI environment")
    64  	}
    65  	ldapContainer, err = prepareLdapContainer()
    66  	require.NoErrorf(t, err, "Ldap Server not available")
    67  	require.NotNil(t, ldapContainer, "Prepare failed")
    68  	defer common.DestroyDockerContainer(ldapContainer)
    69  	server, sslport = common.GetContainerHostAndPort(ldapContainer, "1636/tcp")
    70  
    71  	base := LdapBaseDn
    72  	lc = ldaplib.NewConfig(server, sslport, true, true, base, ldapTimeout)
    73  
    74  	t.Run("Ldap Connect", func(t *testing.T) {
    75  		t.Logf("Connect '%s' using SSL on port %d", LdapTestUserDN, sslport)
    76  		err = lc.Connect(LdapTestUserDN, LdapTestUserPassword)
    77  		require.NoErrorf(t, err, "admin Connect returned error %v", err)
    78  		assert.NotNilf(t, lc.Conn, "Ldap Connect is nil")
    79  		assert.IsType(t, &ldap.Conn{}, lc.Conn, "returned object ist not ldap connection")
    80  		if lc.Conn == nil {
    81  			t.Fatalf("No valid Connection, terminate")
    82  			return
    83  		}
    84  	})
    85  	t.Run("change NonAdmin ssh key", func(t *testing.T) {
    86  		args := []string{
    87  			"ldap",
    88  			"change-sshpubkey",
    89  			"--ldap.host", server,
    90  			"--ldap.port", fmt.Sprintf("%d", sslport),
    91  			"--ldap.tls", "true",
    92  			"--ldap.insecure", "true",
    93  			"--ldap.base", LdapBaseDn,
    94  			"--ldap.binddn", LdapTestUserDN,
    95  			"--ldap.bindpassword", LdapTestUserPassword,
    96  			"--sshpubkeyfile", sshkeyfile,
    97  			"--info",
    98  			"--unit-test",
    99  		}
   100  		out, err = common.CmdRun(RootCmd, args)
   101  		require.NoErrorf(t, err, "Command returned error: %s", err)
   102  		t.Logf(out)
   103  		assert.Containsf(t, out, "SUCCESS: ", "Output not as expected")
   104  		_ = ldapPassCmd.Flags().Set("new_password", "")
   105  	})
   106  	t.Run("set SSH Key by Admin", func(t *testing.T) {
   107  		args := []string{
   108  			"ldap",
   109  			"change-sshpubkey",
   110  			"--ldap.host", server,
   111  			"--ldap.port", fmt.Sprintf("%d", sslport),
   112  			"--ldap.tls", "true",
   113  			"--ldap.insecure", "true",
   114  			"--ldap.base", LdapBaseDn,
   115  			"--ldap.binddn", LdapAdminUser,
   116  			"--ldap.bindpassword", LdapAdminPassword,
   117  			"--ldap.targetdn", LdapTestUserDN,
   118  			"--sshpubkeyfile", sshkeyfile,
   119  			"--info",
   120  			"--unit-test",
   121  		}
   122  		out, err = common.CmdRun(RootCmd, args)
   123  		require.NoErrorf(t, err, "Command returned error: %s", err)
   124  		t.Logf(out)
   125  		assert.Containsf(t, out, "SUCCESS: ", "Output not as expected")
   126  		_ = ldapSSHCmd.Flags().Set("ldap.targetdn", "")
   127  	})
   128  	t.Run("set SSH Key without class", func(t *testing.T) {
   129  		args := []string{
   130  			"ldap",
   131  			"change-sshpubkey",
   132  			"--ldap.host", server,
   133  			"--ldap.port", fmt.Sprintf("%d", sslport),
   134  			"--ldap.tls", "true",
   135  			"--ldap.insecure", "true",
   136  			"--ldap.base", LdapBaseDn,
   137  			"--ldap.binddn", LdapTestUser2DN,
   138  			"--ldap.bindpassword", LdapTestUser2Password,
   139  			"--sshpubkeyfile", sshkeyfile,
   140  			"--info",
   141  			"--unit-test",
   142  		}
   143  		out, err = common.CmdRun(RootCmd, args)
   144  		require.Errorf(t, err, "Command should return error")
   145  		t.Logf(out)
   146  		assert.Containsf(t, out, "objectclass ldapPublicKey not found", "Output not as expected")
   147  		_ = ldapPassCmd.Flags().Set("ldap.targetdn", "")
   148  	})
   149  	t.Run("change NonAdmin Ldap password", func(t *testing.T) {
   150  		args := []string{
   151  			"ldap",
   152  			"change-password",
   153  			"--ldap.host", server,
   154  			"--ldap.port", fmt.Sprintf("%d", sslport),
   155  			"--ldap.tls", "true",
   156  			"--ldap.insecure", "true",
   157  			"--ldap.base", LdapBaseDn,
   158  			"--ldap.binddn", LdapTestUserDN,
   159  			"--ldap.bindpassword", LdapTestUserPassword,
   160  			"--new-password", LdapNewPassword,
   161  			"--ldap.targetdn", "",
   162  			"--info",
   163  			"--unit-test",
   164  		}
   165  		out, err = common.CmdRun(RootCmd, args)
   166  		require.NoErrorf(t, err, "Command returned error: %s", err)
   167  		t.Logf(out)
   168  		assert.Containsf(t, out, "SUCCESS: ", "Output not as expected")
   169  		_ = ldapPassCmd.Flags().Set("new-password", "")
   170  		_ = ldapPassCmd.Flags().Set("ldap.targetdn", "")
   171  	})
   172  	t.Run("change Ldap password by Admin", func(t *testing.T) {
   173  		args := []string{
   174  			"ldap",
   175  			"change-password",
   176  			"--ldap.host", server,
   177  			"--ldap.port", fmt.Sprintf("%d", sslport),
   178  			"--ldap.tls", "true",
   179  			"--ldap.insecure", "true",
   180  			"--ldap.base", LdapBaseDn,
   181  			"--ldap.binddn", LdapAdminUser,
   182  			"--ldap.bindpassword", LdapAdminPassword,
   183  			"--ldap.targetdn", LdapTestUser2DN,
   184  			"--new-password", LdapNewPassword,
   185  			"--info",
   186  			"--unit-test",
   187  		}
   188  		out, err = common.CmdRun(RootCmd, args)
   189  		require.NoErrorf(t, err, "Command returned error: %s", err)
   190  		t.Logf(out)
   191  		assert.Containsf(t, out, "SUCCESS: ", "Output not as expected")
   192  		_ = ldapPassCmd.Flags().Set("new-password", "")
   193  		_ = ldapPassCmd.Flags().Set("ldap.targetdn", "")
   194  	})
   195  	t.Run("change Ldap password by Admin with prompt", func(t *testing.T) {
   196  		args := []string{
   197  			"ldap",
   198  			"change-password",
   199  			"--ldap.host", server,
   200  			"--ldap.port", fmt.Sprintf("%d", sslport),
   201  			"--ldap.tls", "true",
   202  			"--ldap.insecure", "true",
   203  			"--ldap.base", LdapBaseDn,
   204  			"--ldap.binddn", LdapAdminUser,
   205  			"--ldap.bindpassword", LdapAdminPassword,
   206  			"--ldap.targetdn", LdapTestUser2DN,
   207  			// "--new-password", LdapNewPassword,
   208  			"--info",
   209  			"--unit-test",
   210  		}
   211  		_, _ = w.WriteString(fmt.Sprintf("%s\n", LdapNewPassword+"1"))
   212  		time.Sleep(1 * time.Second)
   213  		_, _ = w.WriteString(fmt.Sprintf("%s\n", LdapNewPassword+"1"))
   214  		out, err = common.CmdRun(RootCmd, args)
   215  		require.NoErrorf(t, err, "Command returned error: %s", err)
   216  		t.Logf(out)
   217  		assert.Containsf(t, out, "SUCCESS: ", "Output not as expected")
   218  		_ = ldapPassCmd.Flags().Set("new-password", "")
   219  		_ = ldapPassCmd.Flags().Set("ldap.targetdn", "")
   220  	})
   221  	t.Run("change and generate password", func(t *testing.T) {
   222  		np := ldapPassCmd.Flags().Lookup("new-password")
   223  		np.Changed = false
   224  		args := []string{
   225  			"ldap",
   226  			"change-password",
   227  			"--ldap.host", server,
   228  			"--ldap.port", fmt.Sprintf("%d", sslport),
   229  			"--ldap.tls", "true",
   230  			"--ldap.insecure", "true",
   231  			"--ldap.base", LdapBaseDn,
   232  			"--ldap.binddn", LdapAdminUser,
   233  			"--ldap.bindpassword", LdapAdminPassword,
   234  			"--ldap.targetdn", LdapTestUser2DN,
   235  			"--generate",
   236  			"--profile", "6 1 1 1 0 0",
   237  			"--info",
   238  			"--unit-test",
   239  		}
   240  		out, err = common.CmdRun(RootCmd, args)
   241  		require.NoErrorf(t, err, "Command returned error: %s", err)
   242  		t.Logf(out)
   243  		assert.Containsf(t, out, "SUCCESS: ", "Output not as expected")
   244  		assert.Containsf(t, out, "generated Password: ", "Output not as expected")
   245  		_ = ldapPassCmd.Flags().Set("ldap.targetdn", "")
   246  		targetDN = ""
   247  	})
   248  	t.Run("Show Attributes without basedn", func(t *testing.T) {
   249  		ldapBaseDN = ""
   250  		args := []string{
   251  			"ldap",
   252  			"show",
   253  			"--ldap.host", server,
   254  			"--ldap.port", fmt.Sprintf("%d", sslport),
   255  			"--ldap.tls", "true",
   256  			"--ldap.insecure", "true",
   257  			"--ldap.binddn", LdapAdminUser,
   258  			"--ldap.bindpassword", LdapAdminPassword,
   259  			"--ldap.targetuser", "test2",
   260  			"--info",
   261  			"--unit-test",
   262  		}
   263  		out, err = common.CmdRun(RootCmd, args)
   264  		require.NoErrorf(t, err, "Command returned error: %s", err)
   265  		t.Logf(out)
   266  		assert.Containsf(t, out, "sn: test2", "Output not as expected")
   267  	})
   268  	t.Run("Show groups without bind pass", func(t *testing.T) {
   269  		ldapBaseDN = ""
   270  		ldapBindPassword = ""
   271  		args := []string{
   272  			"ldap",
   273  			"groups",
   274  			"--ldap.host", server,
   275  			"--ldap.port", fmt.Sprintf("%d", sslport),
   276  			"--ldap.tls", "true",
   277  			"--ldap.insecure", "true",
   278  			"--ldap.binddn", LdapAdminUser,
   279  			// "--ldap.bindpassword", LdapAdminPassword,
   280  			"--ldap.targetuser", "test2",
   281  			"--info",
   282  			"--unit-test",
   283  		}
   284  
   285  		// write to Stdin
   286  		_, _ = w.WriteString(fmt.Sprintf("%s\n", LdapAdminPassword))
   287  		out, err = common.CmdRun(RootCmd, args)
   288  		require.NoErrorf(t, err, "Command returned error: %s", err)
   289  		t.Logf(out)
   290  		assert.Containsf(t, out, "Group: cn=ssh,ou=Groups,dc=example,dc=local", "Output not as expected")
   291  	})
   292  	_ = w.Close()
   293  }
   294  
   295  func TestPromptPassword(t *testing.T) {
   296  	// redirect Stdin
   297  	oldStdin := os.Stdin
   298  	re, wr, _ := os.Pipe()
   299  	inputReader = re
   300  	// write to Stdin
   301  	_, _ = wr.WriteString("test\n")
   302  	// test promptPassword
   303  	password, err := promptPassword("TestPromptPassword:")
   304  	require.NoError(t, err, "PromptPassword should not return error")
   305  	assert.Equal(t, "test", password, "PromptPassword should return test")
   306  	// restore Stdin
   307  	inputReader = oldStdin
   308  	_ = wr.Close()
   309  }