// Verift that 2 entity list are equal only if all their data is equal
func Test_EntityManagerIsEqual(t *testing.T) {
	names := []string{"g1", "g2", "g3"}
	resourceNames := []string{"r1", "r2"}
	userName := "******"
	len := 3
	var el [3]*EntityManager

	for i := 0; i < len; i++ {
		el[i] = New()
		addEntities(el[i], groupTypeStr, names, true)
		if i > 0 {
			if el[i].IsEqual(el[i-1]) == false {
				t.Errorf("Test fail: entity list %v: %v must be equal to entity list %v %v", i, el[i], i-1, el[i-1])
			}
		}
	}
	a1, _ := am.NewUserAm(am.UserPermission, secret, salt, false)
	el[0].AddPropertyToEntity(getGroupFormat(names[1]), defs.AmPropertyName, a1)
	el[2].AddPropertyToEntity(getGroupFormat(names[1]), defs.AmPropertyName, a1)
	el[1].AddUser(userName)
	el[1].AddUserToGroup(getGroupFormat(names[1]), userName)
	logger.Trace.Println("Entity data", el[1].String(), el[1].GetGroupUsers(getGroupFormat(names[1])))
	if el[1].IsUserPartOfAGroup(getGroupFormat(names[1]), userName) == false {
		t.Errorf("user '%v' should be part of group %v in entity %v\n", userName, getGroupFormat(names[1]), el[1])
	}
	if el[1].IsUserPartOfAGroup(names[0], userName) == true {
		t.Errorf("user '%v' should not be part of group entity %v\n", userName, el[1])
	}
	el[2].RemovePropertyFromEntity(getGroupFormat(names[1]), defs.AmPropertyName)
	for i := 0; i < len; i++ {
		for j := 0; j < len; j++ {
			if i != j && el[i].IsEqual(el[j]) == true {
				t.Errorf("Test fail: entity list %v:\n%v is not equal to entity list %v:\n%v", i, el[i].getEntityManagerStrWithProperties(), j, el[j].getEntityManagerStrWithProperties())
			}
		}
	}
	err := el[2].RemovePropertyFromEntity(names[1], defs.AmPropertyName)
	if err == nil {
		t.Errorf("Test fail: successfully removed undefined property %v", names[1])
	}
	el[2].AddPropertyToEntity(names[1], defs.AmPropertyName, a1)
	err = el[2].RemovePropertyFromEntity(names[1], defs.AmPropertyName)
	if err != nil {
		t.Errorf("Test fail: fail to removed property %v from el %v", userName, el[2])
	}
	addEntities(el[2], resourceTypeStr, resourceNames, true)
	el[2].AddPropertyToEntity(resourceNames[0], defs.AmPropertyName, a1)
	err = el[2].RemovePropertyFromEntity(resourceNames[0], defs.AmPropertyName)
	if err != nil {
		t.Errorf("Test fail: fail to removed property %v from el %v, error: %v", resourceNames[0], el[2], err)
	}
	err = el[2].RemovePropertyFromEntity("undef1", defs.AmPropertyName)
	if err == nil {
		t.Errorf("Test fail: Successfully removed undefined property from el %v", el[2])
	}
}
// Generate a new secure storage minimal file that includes the root user with
// basic Account Management: the root user privilege and password
func createBasicFile(stFilePath string, name string, pass string, key []byte) {
	saltStr, _ := salt.GetRandomSalt(saltLen)
	_, err := salt.GenerateSaltedPassword([]byte(pass), password.MinPasswordLength, password.MaxPasswordLength, saltStr, -1)
	if err != nil {
		log.Fatalf("Error: can't generate salted password for '%v' user, error: %v", name, err)
	}
	ul := en.New()
	ul.AddUser(name)
	amUser, _ := am.NewUserAm(am.SuperUserPermission, []byte(pass), saltStr, true)
	ul.AddPropertyToEntity(name, defs.AmPropertyName, amUser)
	ul.StoreInfo(stFilePath, key, false)
}
func GenerateUserData(el *EntityManager, usersName []string, secret []byte, salt []byte) {
	el.AddUser(usersName[0])
	el.AddResource("r" + usersName[0])
	amData, _ := am.NewUserAm(am.SuperUserPermission, secret, salt, false)
	el.AddPropertyToEntity(usersName[0], defs.AmPropertyName, amData)
	otpData, _ := otp.NewSimpleOtpUser(secret, false)
	el.AddPropertyToEntity(usersName[0], defs.OtpPropertyName, otpData)
	pwdData, _ := password.NewUserPwd(secret, salt, false)
	el.AddPropertyToEntity(usersName[0], defs.PwdPropertyName, pwdData)
	ocraData, _ := ocra.NewOcraUser([]byte("ABCD1234"), "OCRA-1:HOTP-SHA512-8:C-QH08-T1M-S064-PSHA256")
	el.AddPropertyToEntity(usersName[0], defs.OcraPropertyName, ocraData)

	el.AddUser(usersName[1])
	el.AddPropertyToEntity(usersName[1], defs.OtpPropertyName, otpData)
}
func (l AmRestful) restAddAm(request *restful.Request, response *restful.Response) {
	name := request.PathParameter(userIDParam)

	privilege := l.getPrivilegePwd(request, response)
	if privilege == nil {
		return
	}
	saltStr, _ := salt.GetRandomSalt(saltLen)

	data, err := am.NewUserAm(privilege.Privilege, []byte(privilege.Password), saltStr, checkPasswordStrength)
	if err != nil {
		l.setError(response, http.StatusBadRequest, err)
		return
	}
	err = l.st.UsersList.AddPropertyToEntity(name, defs.AmPropertyName, data)
	if err != nil {
		l.setError(response, http.StatusNotFound, err)
		return
	}
	response.WriteHeaderAndEntity(http.StatusCreated, l.getURLPath(request, name))
}
// Verift that entities in entity list retun OK only if the entity is in the list and the password match
// Verify that the throttling delay is the same for cases were the user is not in the list as well as if the password does not match
func Test_VerifyEntityPasswordAndTimmingAttack(t *testing.T) {
	baseName := "a"
	types := []string{userTypeStr, groupTypeStr, resourceTypeStr}
	throttleDelayMiliSec := int64(100)
	randomThrottling := int64(3)
	allowedErrorP := float64(randomThrottling + 1)

	el := New()
	for _, typeStr := range types {
		user := []string{baseName + typeStr}
		_, err := addEntities(el, typeStr, user, true)
		if err != nil {
			t.Errorf("Test fail: %v", err)
			t.FailNow()
		}
		pass := []byte(string(secret) + typeStr)
		a1, _ := am.NewUserAm(am.UserPermission, pass, salt, false)
		el.AddPropertyToEntity(user[0], defs.AmPropertyName, a1)
	}
	for _, typeStr := range types {
		user := baseName + typeStr
		pass := []byte(string(secret) + typeStr)
		for _, typeStr1 := range types {
			user1 := baseName + typeStr1
			start := time.Now()
			_, err := el.GetEntityAccountHandler(user1, pass, throttleDelayMiliSec, randomThrottling)
			totalDelay := float64(time.Since(start) / time.Millisecond)
			errorP := math.Abs(float64(totalDelay/float64(throttleDelayMiliSec))-1) * 100
			if typeStr == typeStr1 && err != nil {
				t.Errorf("Test fail: user %v with pass %v was not found, error: %v", user1, pass, err)
				t.FailNow()
			}
			if typeStr != typeStr1 && errorP > allowedErrorP {
				t.Errorf("Test fail: the throttling %v was not as expected %v with tolerance of %v%%", totalDelay, throttleDelayMiliSec, allowedErrorP)
				t.FailNow()
			}
			if typeStr != typeStr1 && err == nil {
				t.Errorf("Test fail: user %v with pass %v match user %v with different password", user, pass, user1)
				t.FailNow()
			}
		}
	}
	for _, typeStr := range types {
		user := []string{baseName + typeStr}
		pass := []byte(string(secret) + typeStr)
		_, err := removeEntities(el, typeStr, user, true)
		if err != nil {
			t.Errorf("Test fail: %v", err)
		}
		start := time.Now()
		_, err = el.GetEntityAccountHandler(user[0], pass, throttleDelayMiliSec, randomThrottling)
		totalDelay := float64(time.Since(start) / time.Millisecond)
		errorP := math.Abs(float64(totalDelay/float64(throttleDelayMiliSec))-1) * 100
		if err == nil {
			t.Errorf("Test fail: removed user %v with pass %v was found", user, pass)
			t.FailNow()
		} else if err != nil && errorP > allowedErrorP {
			t.Errorf("Test fail: the throttling %v was not as expected %v with tolerance of %v%%", totalDelay, throttleDelayMiliSec, allowedErrorP)
			t.FailNow()
		}
	}
}
func Test_AddCheckRemoveAMUserProperty(t *testing.T) {
	moduleData, _ := am.NewUserAm(am.SuperUserPermission, secret, salt, false)

	testAddCheckRemoveUserProperty(t, defs.AmPropertyName, moduleData)
}