Make pwdLastSet stuff more generic and not require parsing the timestamp
Signed-off-by: Margo Crawford <margaretc@vmware.com>
This commit is contained in:
parent
65f3464995
commit
acaad05341
@ -318,7 +318,7 @@ func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context,
|
|||||||
Dialer: c.ldapDialer,
|
Dialer: c.ldapDialer,
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
upstreamldap.PwdLastSetAttribute: upstreamldap.PwdUnchangedSinceLogin,
|
upstreamldap.PwdLastSetAttribute: upstreamldap.AttributeUnchangedSinceLogin(upstreamldap.PwdLastSetAttribute),
|
||||||
upstreamldap.UserAccountControlAttribute: upstreamldap.ValidUserAccountControl,
|
upstreamldap.UserAccountControlAttribute: upstreamldap.ValidUserAccountControl,
|
||||||
upstreamldap.UserAccountControlComputedAttribute: upstreamldap.ValidComputedUserAccountControl,
|
upstreamldap.UserAccountControlComputedAttribute: upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
|
@ -222,7 +222,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -543,7 +543,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -604,7 +604,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -668,7 +668,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -732,7 +732,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -795,7 +795,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -929,7 +929,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -1058,7 +1058,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
}},
|
}},
|
||||||
@ -1113,7 +1113,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -1318,7 +1318,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
GroupAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"sAMAccountName": upstreamldap.GroupSAMAccountNameWithDomainSuffix},
|
GroupAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"sAMAccountName": upstreamldap.GroupSAMAccountNameWithDomainSuffix},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -1375,7 +1375,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -1436,7 +1436,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -1491,7 +1491,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
@ -1692,7 +1692,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": upstreamldap.MicrosoftUUIDFromBinary("objectGUID")},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
"pwdLastSet": upstreamldap.PwdUnchangedSinceLogin,
|
"pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"),
|
||||||
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
"userAccountControl": upstreamldap.ValidUserAccountControl,
|
||||||
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
"msDS-User-Account-Control-Computed": upstreamldap.ValidComputedUserAccountControl,
|
||||||
},
|
},
|
||||||
|
@ -348,20 +348,47 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
|||||||
"upstreamRefreshToken": "嵽痊w©Ź榨Q|ôɵt毇妬"
|
"upstreamRefreshToken": "嵽痊w©Ź榨Q|ôɵt毇妬"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"userDN": "6鉢緋uƴŤȱʀļÂ?墖\u003cƬb獭潜Ʃ饾"
|
"userDN": "6鉢緋uƴŤȱʀļÂ?墖\u003cƬb獭潜Ʃ饾",
|
||||||
|
"extraRefreshAttributes": {
|
||||||
|
"ĝ": [
|
||||||
|
"IȽ齤士bEǎ",
|
||||||
|
"跞@)¿,ɭS隑ip偶宾儮猷V麹"
|
||||||
|
],
|
||||||
|
"齁š%OpKȱ藚ɏ¬Ê蒭堜]ȗ韚ʫ": [
|
||||||
|
"aŚB碠k9帴ʘ赱ŕ瑹xȢ~1Į",
|
||||||
|
"睋邔\u0026Ű惫蜀Ģ¡圔鎥墀jMʥ",
|
||||||
|
"+î艔垎0"
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"activedirectory": {
|
"activedirectory": {
|
||||||
"userDN": "|鬌R蜚蠣麹概÷驣7Ʀ澉1æɽ誮rʨ鷞"
|
"userDN": "ȝƋ鬯犦獢9c5¤.岵",
|
||||||
|
"extraRefreshAttributes": {
|
||||||
|
"\u0026錝D肁Ŷɽ蔒PR}Ųʓ": [
|
||||||
|
"_º$+溪ŸȢŒų崓ļ"
|
||||||
|
],
|
||||||
|
"P姧骦:駝重Eȫ": [
|
||||||
|
"ɂ/",
|
||||||
|
"Ƀɫ囤",
|
||||||
|
"鉌X縆跣ŠɞɮƎ賿礣©硇"
|
||||||
|
],
|
||||||
|
"a齙\\蹼偦歛ơ": [
|
||||||
|
"y衑拁Ȃ縅",
|
||||||
|
"Vƅȭǝ*擦28Dž ",
|
||||||
|
"ã置bņ抰蛖"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"requestedAudience": [
|
"requestedAudience": [
|
||||||
"ŚB碠k9"
|
"õC嶃",
|
||||||
|
"ɣƜ/気ū齢q萮左/篣AÚƄŕ~čfV",
|
||||||
|
"x荃墎]ac["
|
||||||
],
|
],
|
||||||
"grantedAudience": [
|
"grantedAudience": [
|
||||||
"ʘ赱",
|
"XôĖ给溬d鞕",
|
||||||
"ď逳鞪?3)藵睋邔\u0026Ű惫蜀Ģ¡圔",
|
"腿tʏƲ%}ſ¯Ɣ 籌Tǘ乚Ȥ2Ķě"
|
||||||
"墀jMʥ"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version": "2"
|
"version": "2"
|
||||||
|
@ -396,6 +396,7 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) {
|
|||||||
// while the fuzzer will panic if AuthorizeRequest changes in a way that cannot be fuzzed,
|
// while the fuzzer will panic if AuthorizeRequest changes in a way that cannot be fuzzed,
|
||||||
// if it adds a new field that can be fuzzed, this check will fail
|
// if it adds a new field that can be fuzzed, this check will fail
|
||||||
// thus if AuthorizeRequest changes, we will detect it here (though we could possibly miss an omitempty field)
|
// thus if AuthorizeRequest changes, we will detect it here (though we could possibly miss an omitempty field)
|
||||||
|
t.Log(authorizeCodeSessionJSONFromFuzzing)
|
||||||
require.JSONEq(t, ExpectedAuthorizeCodeSessionJSONFromFuzzing, authorizeCodeSessionJSONFromFuzzing)
|
require.JSONEq(t, ExpectedAuthorizeCodeSessionJSONFromFuzzing, authorizeCodeSessionJSONFromFuzzing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,11 +123,13 @@ func handleAuthRequestForLDAPUpstream(
|
|||||||
if idpType == psession.ProviderTypeLDAP {
|
if idpType == psession.ProviderTypeLDAP {
|
||||||
customSessionData.LDAP = &psession.LDAPSessionData{
|
customSessionData.LDAP = &psession.LDAPSessionData{
|
||||||
UserDN: dn,
|
UserDN: dn,
|
||||||
|
ExtraRefreshAttributes: authenticateResponse.User.GetExtra(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if idpType == psession.ProviderTypeActiveDirectory {
|
if idpType == psession.ProviderTypeActiveDirectory {
|
||||||
customSessionData.ActiveDirectory = &psession.ActiveDirectorySessionData{
|
customSessionData.ActiveDirectory = &psession.ActiveDirectorySessionData{
|
||||||
UserDN: dn,
|
UserDN: dn,
|
||||||
|
ExtraRefreshAttributes: authenticateResponse.User.GetExtra(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +283,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
Name: happyLDAPUsernameFromAuthenticator,
|
Name: happyLDAPUsernameFromAuthenticator,
|
||||||
UID: happyLDAPUID,
|
UID: happyLDAPUID,
|
||||||
Groups: happyLDAPGroups,
|
Groups: happyLDAPGroups,
|
||||||
|
Extra: map[string][]string{"some-refresh-attribute": {"some-refresh-attribute-value"}},
|
||||||
},
|
},
|
||||||
DN: happyLDAPUserDN,
|
DN: happyLDAPUserDN,
|
||||||
}, true, nil
|
}, true, nil
|
||||||
@ -443,6 +444,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
LDAP: nil,
|
LDAP: nil,
|
||||||
ActiveDirectory: &psession.ActiveDirectorySessionData{
|
ActiveDirectory: &psession.ActiveDirectorySessionData{
|
||||||
UserDN: happyLDAPUserDN,
|
UserDN: happyLDAPUserDN,
|
||||||
|
ExtraRefreshAttributes: map[string][]string{"some-refresh-attribute": {"some-refresh-attribute-value"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,6 +455,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
|||||||
OIDC: nil,
|
OIDC: nil,
|
||||||
LDAP: &psession.LDAPSessionData{
|
LDAP: &psession.LDAPSessionData{
|
||||||
UserDN: happyLDAPUserDN,
|
UserDN: happyLDAPUserDN,
|
||||||
|
ExtraRefreshAttributes: map[string][]string{"some-refresh-attribute": {"some-refresh-attribute-value"}},
|
||||||
},
|
},
|
||||||
ActiveDirectory: nil,
|
ActiveDirectory: nil,
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,7 @@ type StoredRefreshAttributes struct {
|
|||||||
Subject string
|
Subject string
|
||||||
DN string
|
DN string
|
||||||
AuthTime time.Time
|
AuthTime time.Time
|
||||||
|
AdditionalAttributes map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DynamicUpstreamIDPProvider interface {
|
type DynamicUpstreamIDPProvider interface {
|
||||||
|
@ -180,6 +180,13 @@ func upstreamLDAPRefresh(ctx context.Context, providerCache oidc.UpstreamIdentit
|
|||||||
return errorsx.WithStack(errMissingUpstreamSessionInternalError)
|
return errorsx.WithStack(errMissingUpstreamSessionInternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var additionalAttributes map[string][]string
|
||||||
|
if s.ProviderType == psession.ProviderTypeLDAP {
|
||||||
|
additionalAttributes = s.LDAP.ExtraRefreshAttributes
|
||||||
|
} else {
|
||||||
|
additionalAttributes = s.ActiveDirectory.ExtraRefreshAttributes
|
||||||
|
}
|
||||||
|
|
||||||
// get ldap/ad provider out of cache
|
// get ldap/ad provider out of cache
|
||||||
p, dn, err := findLDAPProviderByNameAndValidateUID(s, providerCache)
|
p, dn, err := findLDAPProviderByNameAndValidateUID(s, providerCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -194,6 +201,7 @@ func upstreamLDAPRefresh(ctx context.Context, providerCache oidc.UpstreamIdentit
|
|||||||
Subject: subject,
|
Subject: subject,
|
||||||
DN: dn,
|
DN: dn,
|
||||||
AuthTime: session.IDTokenClaims().AuthTime,
|
AuthTime: session.IDTokenClaims().AuthTime,
|
||||||
|
AdditionalAttributes: additionalAttributes,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorsx.WithStack(errUpstreamRefreshError.WithHint(
|
return errorsx.WithStack(errUpstreamRefreshError.WithHint(
|
||||||
|
@ -67,11 +67,13 @@ type OIDCSessionData struct {
|
|||||||
// LDAPSessionData is the additional data needed by Pinniped when the upstream IDP is an LDAP provider.
|
// LDAPSessionData is the additional data needed by Pinniped when the upstream IDP is an LDAP provider.
|
||||||
type LDAPSessionData struct {
|
type LDAPSessionData struct {
|
||||||
UserDN string `json:"userDN"`
|
UserDN string `json:"userDN"`
|
||||||
|
ExtraRefreshAttributes map[string][]string `json:"extraRefreshAttributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActiveDirectorySessionData is the additional data needed by Pinniped when the upstream IDP is an Active Directory provider.
|
// ActiveDirectorySessionData is the additional data needed by Pinniped when the upstream IDP is an Active Directory provider.
|
||||||
type ActiveDirectorySessionData struct {
|
type ActiveDirectorySessionData struct {
|
||||||
UserDN string `json:"userDN"`
|
UserDN string `json:"userDN"`
|
||||||
|
ExtraRefreshAttributes map[string][]string `json:"extraRefreshAttributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPinnipedSession returns a new empty session.
|
// NewPinnipedSession returns a new empty session.
|
||||||
|
@ -593,6 +593,15 @@ func (p *Provider) searchAndBindUser(conn Conn, username string, bindFunc func(c
|
|||||||
}
|
}
|
||||||
sort.Strings(mappedGroupNames)
|
sort.Strings(mappedGroupNames)
|
||||||
|
|
||||||
|
mappedRefreshAttributes := make(map[string][]string)
|
||||||
|
for k := range p.c.RefreshAttributeChecks {
|
||||||
|
mappedVal, err := p.getSearchResultAttributeValue(k, userEntry, username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mappedRefreshAttributes[k] = []string{mappedVal}
|
||||||
|
}
|
||||||
|
|
||||||
// Caution: Note that any other LDAP commands after this bind will be run as this user instead of as the configured BindUsername!
|
// Caution: Note that any other LDAP commands after this bind will be run as this user instead of as the configured BindUsername!
|
||||||
err = bindFunc(conn, userEntry.DN)
|
err = bindFunc(conn, userEntry.DN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -615,6 +624,7 @@ func (p *Provider) searchAndBindUser(conn Conn, username string, bindFunc func(c
|
|||||||
Name: mappedUsername,
|
Name: mappedUsername,
|
||||||
UID: mappedUID,
|
UID: mappedUID,
|
||||||
Groups: mappedGroupNames,
|
Groups: mappedGroupNames,
|
||||||
|
Extra: mappedRefreshAttributes,
|
||||||
},
|
},
|
||||||
DN: userEntry.DN,
|
DN: userEntry.DN,
|
||||||
}
|
}
|
||||||
@ -676,7 +686,7 @@ func (p *Provider) refreshUserSearchRequest(dn string) *ldap.SearchRequest {
|
|||||||
TimeLimit: 90,
|
TimeLimit: 90,
|
||||||
TypesOnly: false,
|
TypesOnly: false,
|
||||||
Filter: "(objectClass=*)", // we already have the dn, so the filter doesn't matter
|
Filter: "(objectClass=*)", // we already have the dn, so the filter doesn't matter
|
||||||
Attributes: p.refreshAttributes(),
|
Attributes: p.userSearchRequestedAttributes(),
|
||||||
Controls: nil, // this could be used to enable paging, but we're already limiting the result max size
|
Controls: nil, // this could be used to enable paging, but we're already limiting the result max size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -689,6 +699,9 @@ func (p *Provider) userSearchRequestedAttributes() []string {
|
|||||||
if p.c.UserSearch.UIDAttribute != distinguishedNameAttributeName {
|
if p.c.UserSearch.UIDAttribute != distinguishedNameAttributeName {
|
||||||
attributes = append(attributes, p.c.UserSearch.UIDAttribute)
|
attributes = append(attributes, p.c.UserSearch.UIDAttribute)
|
||||||
}
|
}
|
||||||
|
for k := range p.c.RefreshAttributeChecks {
|
||||||
|
attributes = append(attributes, k)
|
||||||
|
}
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,14 +716,6 @@ func (p *Provider) groupSearchRequestedAttributes() []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) refreshAttributes() []string {
|
|
||||||
attributes := p.userSearchRequestedAttributes()
|
|
||||||
for k := range p.c.RefreshAttributeChecks {
|
|
||||||
attributes = append(attributes, k)
|
|
||||||
}
|
|
||||||
return attributes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provider) userSearchFilter(username string) string {
|
func (p *Provider) userSearchFilter(username string) string {
|
||||||
safeUsername := p.escapeUsernameForSearchFilter(username)
|
safeUsername := p.escapeUsernameForSearchFilter(username)
|
||||||
if len(p.c.UserSearch.Filter) == 0 {
|
if len(p.c.UserSearch.Filter) == 0 {
|
||||||
@ -869,43 +874,22 @@ func getDomainFromDistinguishedName(distinguishedName string) (string, error) {
|
|||||||
return strings.Join(domainComponents[1:], "."), nil
|
return strings.Join(domainComponents[1:], "."), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PwdUnchangedSinceLogin(entry *ldap.Entry, attributes provider.StoredRefreshAttributes) error {
|
func AttributeUnchangedSinceLogin(attribute string) func(*ldap.Entry, provider.StoredRefreshAttributes) error {
|
||||||
authTime := attributes.AuthTime
|
return func(entry *ldap.Entry, storedAttributes provider.StoredRefreshAttributes) error {
|
||||||
pwdLastSetWin32Format := entry.GetAttributeValues(PwdLastSetAttribute)
|
prevAttributeValues := storedAttributes.AdditionalAttributes[attribute]
|
||||||
if len(pwdLastSetWin32Format) != 1 {
|
newValues := entry.GetAttributeValues(attribute)
|
||||||
return fmt.Errorf("expected to find 1 value for %s attribute, but found %d", PwdLastSetAttribute, len(pwdLastSetWin32Format))
|
|
||||||
}
|
|
||||||
// convert to a time.Time
|
|
||||||
pwdLastSetParsed, err := win32timestampToTime(pwdLastSetWin32Format[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if pwdLastSet > authTime, that means that the password has been changed since the initial login.
|
if len(newValues) != 1 {
|
||||||
// return an error so the user is prompted to log in again.
|
return fmt.Errorf(`expected to find 1 value for "%s" attribute, but found %d`, attribute, len(newValues))
|
||||||
if pwdLastSetParsed.After(authTime) {
|
}
|
||||||
return fmt.Errorf("password has changed since login. login time: %s, password set time: %s", authTime, pwdLastSetParsed)
|
if len(prevAttributeValues) != 1 {
|
||||||
|
return fmt.Errorf(`expected to find 1 stored value for "%s" attribute, but found %d`, attribute, len(prevAttributeValues))
|
||||||
|
}
|
||||||
|
if prevAttributeValues[0] != newValues[0] {
|
||||||
|
return fmt.Errorf(`value for attribute "%s" has changed since initial value at login. Previous value: "%s", new value: "%s"`, attribute, prevAttributeValues[0], newValues[0])
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func win32timestampToTime(win32timestamp string) (time.Time, error) {
|
|
||||||
// take a win32 timestamp (represented as the number of 100 ns intervals since
|
|
||||||
// January 1, 1601) and make a time.Time
|
|
||||||
|
|
||||||
const unixTimeBaseAsWin = 116_444_736_000_000_000 // The unix base time (January 1, 1970 UTC) as 100 ns since Win32 epoch (1601-01-01)
|
|
||||||
const hundredNsToSecFactor = 10_000_000
|
|
||||||
|
|
||||||
win32Time, err := strconv.ParseInt(win32timestamp, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, fmt.Errorf("couldn't parse as timestamp")
|
|
||||||
}
|
|
||||||
|
|
||||||
unixsec := (win32Time - unixTimeBaseAsWin) / hundredNsToSecFactor
|
|
||||||
unixns := (win32Time % hundredNsToSecFactor) * 100
|
|
||||||
|
|
||||||
convertedTime := time.Unix(unixsec, unixns).UTC()
|
|
||||||
return convertedTime, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidUserAccountControl(entry *ldap.Entry, _ provider.StoredRefreshAttributes) error {
|
func ValidUserAccountControl(entry *ldap.Entry, _ provider.StoredRefreshAttributes) error {
|
||||||
|
@ -160,6 +160,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
Name: testUserSearchResultUsernameAttributeValue,
|
Name: testUserSearchResultUsernameAttributeValue,
|
||||||
UID: base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultUIDAttributeValue)),
|
UID: base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultUIDAttributeValue)),
|
||||||
Groups: []string{testGroupSearchResultGroupNameAttributeValue1, testGroupSearchResultGroupNameAttributeValue2},
|
Groups: []string{testGroupSearchResultGroupNameAttributeValue1, testGroupSearchResultGroupNameAttributeValue2},
|
||||||
|
Extra: map[string][]string{},
|
||||||
}
|
}
|
||||||
if editFunc != nil {
|
if editFunc != nil {
|
||||||
editFunc(u)
|
editFunc(u)
|
||||||
@ -507,6 +508,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
Name: testUserSearchResultUsernameAttributeValue,
|
Name: testUserSearchResultUsernameAttributeValue,
|
||||||
UID: base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultUIDAttributeValue)),
|
UID: base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultUIDAttributeValue)),
|
||||||
Groups: []string{"a", "b", "c"},
|
Groups: []string{"a", "b", "c"},
|
||||||
|
Extra: map[string][]string{},
|
||||||
},
|
},
|
||||||
DN: testUserSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
},
|
},
|
||||||
@ -610,6 +612,66 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
r.Groups = []string{"Animals@activedirectory.mycompany.example.com", "Mammals@activedirectory.mycompany.example.com"}
|
r.Groups = []string{"Animals@activedirectory.mycompany.example.com", "Mammals@activedirectory.mycompany.example.com"}
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "requesting additional refresh related attributes",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
|
p.RefreshAttributeChecks = map[string]func(entry *ldap.Entry, attributes provider.StoredRefreshAttributes) error{
|
||||||
|
"some-attribute-to-check-during-refresh": func(entry *ldap.Entry, attributes provider.StoredRefreshAttributes) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(func(r *ldap.SearchRequest) {
|
||||||
|
r.Attributes = append(r.Attributes, "some-attribute-to-check-during-refresh")
|
||||||
|
})).Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testUserSearchResultDNValue,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testUserSearchResultUsernameAttributeValue}),
|
||||||
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testUserSearchResultUIDAttributeValue}),
|
||||||
|
ldap.NewEntryAttribute("some-attribute-to-check-during-refresh", []string{"some-attribute-value"}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(func(r *user.DefaultInfo) {
|
||||||
|
r.Extra = map[string][]string{"some-attribute-to-check-during-refresh": {"some-attribute-value"}}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "requesting additional refresh related attributes, but they aren't returned",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
|
p.RefreshAttributeChecks = map[string]func(entry *ldap.Entry, attributes provider.StoredRefreshAttributes) error{
|
||||||
|
"some-attribute-to-check-during-refresh": func(entry *ldap.Entry, attributes provider.StoredRefreshAttributes) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(func(r *ldap.SearchRequest) {
|
||||||
|
r.Attributes = append(r.Attributes, "some-attribute-to-check-during-refresh")
|
||||||
|
})).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
wantError: "found 0 values for attribute \"some-attribute-to-check-during-refresh\" while searching for user \"some-upstream-username\", but expected 1 result",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "override group parsing when domain can't be determined from dn",
|
name: "override group parsing when domain can't be determined from dn",
|
||||||
username: testUpstreamUsername,
|
username: testUpstreamUsername,
|
||||||
@ -1265,7 +1327,9 @@ func TestUpstreamRefresh(t *testing.T) {
|
|||||||
UIDAttribute: testUserSearchUIDAttribute,
|
UIDAttribute: testUserSearchUIDAttribute,
|
||||||
UsernameAttribute: testUserSearchUsernameAttribute,
|
UsernameAttribute: testUserSearchUsernameAttribute,
|
||||||
},
|
},
|
||||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{pwdLastSetAttribute: PwdUnchangedSinceLogin},
|
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{
|
||||||
|
pwdLastSetAttribute: AttributeUnchangedSinceLogin(pwdLastSetAttribute),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -1520,7 +1584,7 @@ func TestUpstreamRefresh(t *testing.T) {
|
|||||||
wantErr: "found 2 values for attribute \"some-upstream-uid-attribute\" while searching for user \"some-upstream-user-dn\", but expected 1 result",
|
wantErr: "found 2 values for attribute \"some-upstream-uid-attribute\" while searching for user \"some-upstream-user-dn\", but expected 1 result",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "search result has a recent pwdLastSet value",
|
name: "search result has a changed pwdLastSet value",
|
||||||
providerConfig: providerConfig,
|
providerConfig: providerConfig,
|
||||||
setupMocks: func(conn *mockldapconn.MockConn) {
|
setupMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
@ -1539,7 +1603,7 @@ func TestUpstreamRefresh(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: pwdLastSetAttribute,
|
Name: pwdLastSetAttribute,
|
||||||
Values: []string{"132803468800000000"},
|
Values: []string{"132801740800000001"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1547,7 +1611,7 @@ func TestUpstreamRefresh(t *testing.T) {
|
|||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
wantErr: "validation for attribute \"pwdLastSet\" failed during upstream refresh: password has changed since login. login time: 2021-11-01 23:43:19 +0000 UTC, password set time: 2021-11-02 17:14:40 +0000 UTC",
|
wantErr: "validation for attribute \"pwdLastSet\" failed during upstream refresh: value for attribute \"pwdLastSet\" has changed since initial value at login. Previous value: \"132801740800000000\", new value: \"132801740800000001\"",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1581,6 +1645,7 @@ func TestUpstreamRefresh(t *testing.T) {
|
|||||||
Subject: subject,
|
Subject: subject,
|
||||||
DN: testUserSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
AuthTime: authTime,
|
AuthTime: authTime,
|
||||||
|
AdditionalAttributes: map[string][]string{pwdLastSetAttribute: {"132801740800000000"}},
|
||||||
})
|
})
|
||||||
if tt.wantErr != "" {
|
if tt.wantErr != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@ -1960,148 +2025,67 @@ func TestGetDomainFromDistinguishedName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPwdUnchangedSinceLogin(t *testing.T) {
|
func TestAttributeUnchangedSinceLogin(t *testing.T) {
|
||||||
authTime := "2021-11-01T23:43:19.826433579Z" // this is the format that fosite automatically stores
|
initialVal := "some-attribute-value"
|
||||||
authTimeParsed, err := time.Parse(time.RFC3339Nano, authTime)
|
changedVal := "some-different-attribute-value"
|
||||||
require.NoError(t, err)
|
attributeName := "some-attribute-name"
|
||||||
pwdResetTimeAfterAuthTime := "132803468800000000" // Nov 2
|
|
||||||
pwdResetTimeBeforeAuthTime := "132801740800000000" // Oct 31
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
authTime *time.Time
|
|
||||||
entry *ldap.Entry
|
entry *ldap.Entry
|
||||||
wantResult bool
|
wantResult bool
|
||||||
wantErr string
|
wantErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy path where password has not been reset since login",
|
name: "happy path where value has not changed since login",
|
||||||
authTime: &authTimeParsed,
|
|
||||||
entry: &ldap.Entry{
|
entry: &ldap.Entry{
|
||||||
DN: "some-dn",
|
DN: "some-dn",
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
{
|
{
|
||||||
Name: "pwdLastSet",
|
Name: attributeName,
|
||||||
Values: []string{pwdResetTimeBeforeAuthTime},
|
Values: []string{initialVal},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "password has been reset since login",
|
name: "password has been reset since login",
|
||||||
authTime: &authTimeParsed,
|
|
||||||
entry: &ldap.Entry{
|
entry: &ldap.Entry{
|
||||||
DN: "some-dn",
|
DN: "some-dn",
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
{
|
{
|
||||||
Name: "pwdLastSet",
|
Name: attributeName,
|
||||||
Values: []string{pwdResetTimeAfterAuthTime},
|
Values: []string{changedVal},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantErr: "password has changed since login. login time: 2021-11-01 23:43:19.826433579 +0000 UTC, password set time: 2021-11-02 17:14:40 +0000 UTC",
|
wantErr: "value for attribute \"some-attribute-name\" has changed since initial value at login. Previous value: \"some-attribute-value\", new value: \"some-different-attribute-value\"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ldap timestamp is in the wrong format",
|
name: "no value for attribute attribute",
|
||||||
authTime: &authTimeParsed,
|
|
||||||
entry: &ldap.Entry{
|
|
||||||
DN: "some-dn",
|
|
||||||
Attributes: []*ldap.EntryAttribute{
|
|
||||||
{
|
|
||||||
Name: "pwdLastSet",
|
|
||||||
Values: []string{"invalid"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: "couldn't parse as timestamp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no value for pwdLastSet attribute",
|
|
||||||
authTime: &authTimeParsed,
|
|
||||||
entry: &ldap.Entry{
|
entry: &ldap.Entry{
|
||||||
DN: "some-dn",
|
DN: "some-dn",
|
||||||
Attributes: []*ldap.EntryAttribute{},
|
Attributes: []*ldap.EntryAttribute{},
|
||||||
},
|
},
|
||||||
wantErr: "expected to find 1 value for pwdLastSet attribute, but found 0",
|
wantErr: "expected to find 1 value for \"some-attribute-name\" attribute, but found 0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "too many values for pwdLastSet attribute",
|
name: "too many values for attribute",
|
||||||
authTime: &authTimeParsed,
|
|
||||||
entry: &ldap.Entry{
|
entry: &ldap.Entry{
|
||||||
DN: "some-dn",
|
DN: "some-dn",
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
{
|
{
|
||||||
Name: "pwdLastSet",
|
Name: attributeName,
|
||||||
Values: []string{"val1", "val2"},
|
Values: []string{"val1", "val2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantErr: "expected to find 1 value for pwdLastSet attribute, but found 2",
|
wantErr: "expected to find 1 value for \"some-attribute-name\" attribute, but found 2",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tt := test
|
tt := test
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
err := PwdUnchangedSinceLogin(tt.entry, provider.StoredRefreshAttributes{AuthTime: *tt.authTime})
|
err := AttributeUnchangedSinceLogin(attributeName)(tt.entry, provider.StoredRefreshAttributes{AdditionalAttributes: map[string][]string{attributeName: {initialVal}}})
|
||||||
if tt.wantErr != "" {
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Equal(t, tt.wantErr, err.Error())
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWin32TimestampToTime(t *testing.T) {
|
|
||||||
happyPasswordChangeTime := time.Date(2021, time.January, 2, 0, 12, 21, 0, time.UTC).UTC()
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
timestampString string
|
|
||||||
wantTime time.Time
|
|
||||||
wantErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "happy case with a valid timestamp",
|
|
||||||
timestampString: "132540199410000000",
|
|
||||||
wantTime: happyPasswordChangeTime,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "handles error with a string thats not a timestamp",
|
|
||||||
timestampString: "not timestamp",
|
|
||||||
wantErr: "couldn't parse as timestamp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "handles error with too big of a timestamp",
|
|
||||||
timestampString: "132540199410000000000",
|
|
||||||
wantErr: "couldn't parse as timestamp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "timestamp of zero",
|
|
||||||
timestampString: "0",
|
|
||||||
wantTime: time.Date(1601, time.January, 1, 0, 0, 0, 0, time.UTC).UTC(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "fractional seconds",
|
|
||||||
timestampString: "132540199410000001",
|
|
||||||
wantTime: time.Date(2021, time.January, 2, 0, 12, 21, 100, time.UTC).UTC(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "max allowable value",
|
|
||||||
timestampString: "9223372036854775807", // 2^63-1
|
|
||||||
wantTime: time.Date(30828, time.September, 14, 2, 48, 5, 477580700, time.UTC).UTC(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "just past max allowable value",
|
|
||||||
timestampString: "9223372036854775808", // 2^63
|
|
||||||
wantErr: "couldn't parse as timestamp",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
tt := test
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
actualTime, err := win32timestampToTime(tt.timestampString)
|
|
||||||
require.Equal(t, tt.wantTime, actualTime)
|
|
||||||
if tt.wantErr != "" {
|
if tt.wantErr != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, tt.wantErr, err.Error())
|
require.Equal(t, tt.wantErr, err.Error())
|
||||||
|
Loading…
Reference in New Issue
Block a user