mirror of https://gogs.blitter.com/RLabs/xs
				
				
				
			Added unit tests for auth.go: AuthUserByToken
Signed-off-by: Russ Magee <rmagee@gmail.com>
This commit is contained in:
		
							parent
							
								
									63ab4171c0
								
							
						
					
					
						commit
						3807ddce05
					
				
							
								
								
									
										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,15 +19,39 @@ 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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -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…
	
		Reference in New Issue