mirror of
				https://gogs.blitter.com/RLabs/xs
				synced 2024-08-14 10:26:42 +00:00 
			
		
		
		
	Added unit tests for auth.go: AuthUserByToken
Signed-off-by: Russ Magee <rmagee@gmail.com>
This commit is contained in:
		
							parent
							
								
									3bb9c795e4
								
							
						
					
					
						commit
						f372666187
					
				
					 3 changed files with 93 additions and 15 deletions
				
			
		
							
								
								
									
										34
									
								
								auth.go
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								auth.go
									
										
									
									
									
								
							|  | @ -30,6 +30,9 @@ import ( | |||
| // Verify a password against system standard shadow file | ||||
| // Note auxilliary fields for expiry policy are *not* inspected. | ||||
| func VerifyPass(reader func(string) ([]byte, error), user, password string) (bool, error) { | ||||
| 	if reader == nil { | ||||
| 		reader = ioutil.ReadFile // dependency injection hides that this is required | ||||
| 	} | ||||
| 	passlib.UseDefaults(passlib.Defaults20180601) | ||||
| 	pwFileData, e := reader("/etc/shadow") | ||||
| 	if e != nil { | ||||
|  | @ -70,8 +73,11 @@ func VerifyPass(reader func(string) ([]byte, error), user, password string) (boo | |||
| // This checks /etc/xs.passwd for auth info, and system /etc/passwd | ||||
| // to cross-check the user actually exists. | ||||
| // nolint: gocyclo | ||||
| func AuthUserByPasswd(username string, auth string, fname string) (valid bool, allowedCmds string) { | ||||
| 	b, e := ioutil.ReadFile(fname) // nolint: gosec | ||||
| func AuthUserByPasswd(reader func(string) ([]byte, error), userlookup func(string) (*user.User, error), username string, auth string, fname string) (valid bool, allowedCmds string) { | ||||
| 	if reader == nil { | ||||
| 		reader = ioutil.ReadFile // dependency injection hides that this is required | ||||
| 	} | ||||
| 	b, e := reader(fname) // nolint: gosec | ||||
| 	if e != nil { | ||||
| 		valid = false | ||||
| 		log.Printf("ERROR: Cannot read %s!\n", fname) | ||||
|  | @ -115,7 +121,8 @@ func AuthUserByPasswd(username string, auth string, fname string) (valid bool, a | |||
| 	r = nil | ||||
| 	runtime.GC() | ||||
| 
 | ||||
| 	if !userExistsOnSystem(username) { | ||||
| 	_, userErr := userlookup(username) | ||||
| 	if userErr != nil { | ||||
| 		valid = false | ||||
| 	} | ||||
| 	return | ||||
|  | @ -123,24 +130,26 @@ func AuthUserByPasswd(username string, auth string, fname string) (valid bool, a | |||
| 
 | ||||
| // ------------- End xs-local passwd auth routine(s) ----------- | ||||
| 
 | ||||
| func userExistsOnSystem(who string) bool { | ||||
| 	_, userErr := user.Lookup(who) | ||||
| 	return userErr == nil | ||||
| } | ||||
| 
 | ||||
| // AuthUserByToken checks user login information against an auth token. | ||||
| // Auth tokens are stored in each user's $HOME/.xs_id and are requested | ||||
| // via the -g option. | ||||
| // The function also check system /etc/passwd to cross-check the user | ||||
| // actually exists. | ||||
| func AuthUserByToken(username string, connhostname string, auth string) (valid bool) { | ||||
| func AuthUserByToken(reader func(string) ([]byte, error), userlookup func(string) (*user.User, error), username string, connhostname string, auth string) (valid bool) { | ||||
| 	if reader == nil { | ||||
| 		reader = ioutil.ReadFile // dependency injection hides that this is required | ||||
| 	} | ||||
| 	if userlookup == nil { | ||||
| 		userlookup = user.Lookup // again for dependency injection as dep is now hidden | ||||
| 	} | ||||
| 
 | ||||
| 	auth = strings.TrimSpace(auth) | ||||
| 	u, ue := user.Lookup(username) | ||||
| 	u, ue := userlookup(username) | ||||
| 	if ue != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	b, e := ioutil.ReadFile(fmt.Sprintf("%s/.xs_id", u.HomeDir)) | ||||
| 	b, e := reader(fmt.Sprintf("%s/.xs_id", u.HomeDir)) | ||||
| 	if e != nil { | ||||
| 		log.Printf("INFO: Cannot read %s/.xs_id\n", u.HomeDir) | ||||
| 		return false | ||||
|  | @ -167,7 +176,8 @@ func AuthUserByToken(username string, connhostname string, auth string) (valid b | |||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if !userExistsOnSystem(username) { | ||||
| 	_, userErr := userlookup(username) | ||||
| 	if userErr != nil { | ||||
| 		valid = false | ||||
| 	} | ||||
| 	return | ||||
|  |  | |||
							
								
								
									
										68
									
								
								auth_test.go
									
										
									
									
									
								
							
							
						
						
									
										68
									
								
								auth_test.go
									
										
									
									
									
								
							|  | @ -2,6 +2,9 @@ package xs | |||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os/user" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
|  | @ -16,16 +19,40 @@ var ( | |||
| joebloggs:$6$F.0IXOrb0w0VJHG1$3O4PYyng7F3hlh42mbroEdQZvslybY5etPPiLMQJ1xosjABY.Q4xqAfyIfe03Du61ZjGQIt3nL0j12P9k1fsK/:18310:0:99999:7::: | ||||
| disableduser:!:18310::::::` | ||||
| 
 | ||||
| 	dummyAuthTokenFile = "hostA:abcdefg\nhostB:wxyz\n" | ||||
| 
 | ||||
| 	testGoodUsers = []userVerifs{ | ||||
| 		{"johndoe", "testpass", true}, | ||||
| 		{"joebloggs", "testpass2", true}, | ||||
| 		{"johndoe", "badpass", false}, | ||||
| 	} | ||||
| 
 | ||||
| 	userlookup_arg_u string | ||||
| 	readfile_arg_f   string | ||||
| ) | ||||
| 
 | ||||
| func _mock_user_Lookup(username string) (*user.User, error) { | ||||
| 	username = userlookup_arg_u | ||||
| 	if username == "baduser" { | ||||
| 		return &user.User{}, errors.New("bad user") | ||||
| 	} | ||||
| 	urec := &user.User{Uid: "1000", Gid: "1000", Username: username, Name: "Full Name", HomeDir: "/home/user"} | ||||
| 	fmt.Printf("  [mock user rec:%v]\n", urec) | ||||
| 	return urec, nil | ||||
| } | ||||
| 
 | ||||
| func _mock_ioutil_ReadFile(f string) ([]byte, error) { | ||||
| 	f = readfile_arg_f | ||||
| 	if f == "/etc/shadow" { | ||||
| 		fmt.Println("  [mocking ReadFile(\"/etc/shadow\")]") | ||||
| 		return []byte(dummyShadowA), nil | ||||
| 	} | ||||
| 	if strings.Contains(f, "/.xs_id") { | ||||
| 		fmt.Println("  [mocking ReadFile(\".xs_id\")]") | ||||
| 		return []byte(dummyAuthTokenFile), nil | ||||
| 	} | ||||
| 	return []byte{}, errors.New("no readfile_arg_f supplied") | ||||
| } | ||||
| 
 | ||||
| func _mock_ioutil_ReadFileEmpty(f string) ([]byte, error) { | ||||
| 	return []byte{}, nil | ||||
|  | @ -36,6 +63,7 @@ func _mock_ioutil_ReadFileHasError(f string) ([]byte, error) { | |||
| } | ||||
| 
 | ||||
| func TestVerifyPass(t *testing.T) { | ||||
| 	readfile_arg_f = "/etc/shadow" | ||||
| 	for idx, rec := range testGoodUsers { | ||||
| 		stat, e := VerifyPass(_mock_ioutil_ReadFile, rec.user, rec.passwd) | ||||
| 		if rec.good && (!stat || e != nil) { | ||||
|  | @ -64,3 +92,43 @@ func TestVerifyPassFailsOnDisabledEntry(t *testing.T) { | |||
| 		t.Fatal("failed to fail on disabled user entry") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //// | ||||
| 
 | ||||
| func TestAuthUserByTokenFailsOnMissingEntryForHost(t *testing.T) { | ||||
| 	stat := AuthUserByToken(_mock_ioutil_ReadFile, _mock_user_Lookup, "johndoe", "hostZ", "abcdefg") | ||||
| 	if stat { | ||||
| 		t.Fatal("failed to fail on missing/mismatched host entry") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestAuthUserByTokenFailsOnMissingEntryForUser(t *testing.T) { | ||||
| 	stat := AuthUserByToken(_mock_ioutil_ReadFile, _mock_user_Lookup, "unkuser", "hostA", "abcdefg") | ||||
| 	if stat { | ||||
| 		t.Fatal("failed to fail on wrong user") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestAuthUserByTokenFailsOnUserLookupFailure(t *testing.T) { | ||||
| 	userlookup_arg_u = "baduser" | ||||
| 	stat := AuthUserByToken(_mock_ioutil_ReadFile, _mock_user_Lookup, "johndoe", "hostA", "abcdefg") | ||||
| 	if stat { | ||||
| 		t.Fatal("failed to fail with bad return from user.Lookup()") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestAuthUserByTokenFailsOnMismatchedTokenForUser(t *testing.T) { | ||||
| 	stat := AuthUserByToken(_mock_ioutil_ReadFile, _mock_user_Lookup, "johndoe", "hostA", "badtoken") | ||||
| 	if stat { | ||||
| 		t.Fatal("failed to fail with valid user, bad token") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestAuthUserByTokenSucceedsWithMatchedUserAndToken(t *testing.T) { | ||||
| 	userlookup_arg_u = "johndoe" | ||||
| 	readfile_arg_f = "/.xs_id" | ||||
| 	stat := AuthUserByToken(_mock_ioutil_ReadFile, _mock_user_Lookup, userlookup_arg_u, "hostA", "hostA:abcdefg") | ||||
| 	if !stat { | ||||
| 		t.Fatal("failed with valid user and token") | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -708,14 +708,14 @@ func main() { | |||
| 
 | ||||
| 				var valid bool | ||||
| 				var allowedCmds string // Currently unused | ||||
| 				if xs.AuthUserByToken(string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) { | ||||
| 				if xs.AuthUserByToken(ioutil.ReadFile, user.Lookup, string(rec.Who()), string(rec.ConnHost()), string(rec.AuthCookie(true))) { | ||||
| 					valid = true | ||||
| 				} else { | ||||
| 					if useSystemPasswd { | ||||
| 						//var passErr error | ||||
| 						valid, _ /*passErr*/ = xs.VerifyPass(ioutil.ReadFile, string(rec.Who()), string(rec.AuthCookie(true))) | ||||
| 					} else { | ||||
| 						valid, allowedCmds = xs.AuthUserByPasswd(string(rec.Who()), string(rec.AuthCookie(true)), "/etc/xs.passwd") | ||||
| 						valid, allowedCmds = xs.AuthUserByPasswd(ioutil.ReadFile, user.Lookup, string(rec.Who()), string(rec.AuthCookie(true)), "/etc/xs.passwd") | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue