Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ai3
accountserver
Commits
21a62aa5
Commit
21a62aa5
authored
Dec 05, 2021
by
ale
Browse files
Merge branch 'webauthn' into 'master'
Support Webauthn See merge request
!42
parents
3746d131
b3540bb1
Pipeline
#24851
passed with stages
in 3 minutes and 25 seconds
Changes
223
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
actions.go
View file @
21a62aa5
...
...
@@ -251,7 +251,10 @@ func randomAppSpecificPasswordID() string {
}
func
generateTOTPSecret
()
(
string
,
error
)
{
key
,
err
:=
totp
.
Generate
(
totp
.
GenerateOpts
{})
key
,
err
:=
totp
.
Generate
(
totp
.
GenerateOpts
{
Issuer
:
"accountserver"
,
AccountName
:
"placeholder"
,
})
if
err
!=
nil
{
return
""
,
err
}
...
...
actions_resource.go
View file @
21a62aa5
package
accountserver
import
(
"errors"
"fmt"
"log"
)
...
...
@@ -196,7 +195,7 @@ func (r *CheckResourceAvailabilityRequest) Validate(rctx *RequestContext) error
func
(
r
*
CheckResourceAvailabilityRequest
)
Serve
(
rctx
*
RequestContext
)
(
interface
{},
error
)
{
var
check
ValidatorFunc
switch
r
.
Type
{
case
ResourceTypeEmail
,
ResourceTypeMailingList
:
case
ResourceTypeEmail
,
ResourceTypeMailingList
,
ResourceTypeNewsletter
:
check
=
rctx
.
validationCtx
.
isAvailableEmailAddr
()
case
ResourceTypeDomain
:
check
=
rctx
.
validationCtx
.
isAvailableDomain
()
...
...
@@ -207,7 +206,7 @@ func (r *CheckResourceAvailabilityRequest) Serve(rctx *RequestContext) (interfac
case
ResourceTypeDatabase
:
check
=
rctx
.
validationCtx
.
isAvailableDatabase
()
default
:
return
nil
,
errors
.
New
(
"unknown resource type"
)
return
nil
,
newValidationError
(
nil
,
"type"
,
"unknown resource type"
)
}
var
resp
CheckResourceAvailabilityResponse
...
...
actions_test.go
View file @
21a62aa5
...
...
@@ -22,7 +22,7 @@ type fakeBackend struct {
passwords
map
[
string
]
string
recoveryPasswords
map
[
string
]
string
resourcePasswords
map
[
string
]
string
appSpecificPasswords
map
[
string
][]
*
AppSpecificPassword
Info
appSpecificPasswords
map
[
string
][]
*
ct
.
AppSpecificPassword
encryptionKeys
map
[
string
][]
*
ct
.
EncryptedKey
}
...
...
@@ -175,7 +175,7 @@ func (b *fakeBackend) SetUserEncryptionPublicKey(_ context.Context, user *User,
return
nil
}
func
(
b
*
fakeBackend
)
SetApplicationSpecificPassword
(
_
context
.
Context
,
user
*
User
,
info
*
AppSpecificPassword
Info
,
_
string
)
error
{
func
(
b
*
fakeBackend
)
SetApplicationSpecificPassword
(
_
context
.
Context
,
user
*
User
,
info
*
ct
.
AppSpecificPassword
,
_
string
)
error
{
b
.
appSpecificPasswords
[
user
.
Name
]
=
append
(
b
.
appSpecificPasswords
[
user
.
Name
],
info
)
return
nil
}
...
...
@@ -268,7 +268,7 @@ func createFakeBackend() *fakeBackend {
passwords
:
make
(
map
[
string
]
string
),
recoveryPasswords
:
make
(
map
[
string
]
string
),
resourcePasswords
:
make
(
map
[
string
]
string
),
appSpecificPasswords
:
make
(
map
[
string
][]
*
AppSpecificPassword
Info
),
appSpecificPasswords
:
make
(
map
[
string
][]
*
ct
.
AppSpecificPassword
),
encryptionKeys
:
make
(
map
[
string
][]
*
ct
.
EncryptedKey
),
}
fb
.
addUser
(
&
User
{
...
...
actions_user.go
View file @
21a62aa5
...
...
@@ -2,7 +2,9 @@ package accountserver
import
(
"errors"
"strings"
ct
"git.autistici.org/ai3/go-common/ldap/compositetypes"
umdb
"git.autistici.org/id/usermetadb"
)
...
...
@@ -156,7 +158,6 @@ func (r *AccountRecoveryRequest) Authorize(rctx *RequestContext) error {
return
nil
}
// TODO: call out to auth-server for rate limiting and other features.
// Authenticate the secret recovery password.
if
err
:=
rctx
.
authorizeAccountRecovery
(
rctx
.
Context
,
rctx
.
User
.
Name
,
r
.
RecoveryPassword
,
r
.
RemoteAddr
);
err
!=
nil
{
return
err
...
...
@@ -296,7 +297,7 @@ func (r *CreateApplicationSpecificPasswordRequest) Serve(rctx *RequestContext) (
}
// Create a new application-specific password metadata.
asp
:=
&
AppSpecificPassword
Info
{
asp
:=
&
ct
.
AppSpecificPassword
{
ID
:
randomAppSpecificPasswordID
(),
Service
:
r
.
Service
,
Comment
:
r
.
Notes
,
...
...
@@ -345,7 +346,7 @@ func (r *EnableOTPRequest) Sanitize() {
func
(
r
*
EnableOTPRequest
)
Validate
(
_
*
RequestContext
)
error
{
var
err
*
ValidationError
// Only check if the client-side secret is set, skip otherwise.
if
r
.
TOTPSecret
=
=
""
&&
len
(
r
.
TOTPSecret
)
!=
16
{
if
r
.
TOTPSecret
!
=
""
&&
len
(
r
.
TOTPSecret
)
!=
16
{
err
=
newValidationError
(
err
,
"totp_secret"
,
"bad value"
)
}
return
err
.
orNil
()
...
...
@@ -402,23 +403,28 @@ func (r *DisableOTPRequest) Serve(rctx *RequestContext) (interface{}, error) {
// UpdateUserRequest allows the caller to update a (very limited) selected set
// of fields on a User object. It is a catch-all function for very simple
// changes that don't justify their own specialized method.
// changes that don't justify their own specialized method. Fields are
// associated with a "set_field" attribute to allow for selective updates.
type
UpdateUserRequest
struct
{
UserRequestBase
Lang
string
`json:"lang,omitempty"`
U2FRegistrations
[]
*
U2FRegistration
`json:"u2f_registrations,omitempty"`
Lang
string
`json:"lang,omitempty"`
SetLang
bool
`json:"set_lang"`
U2FRegistrations
[]
*
ct
.
U2FRegistration
`json:"u2f_registrations,omitempty"`
SetU2FRegistrations
bool
`json:"set_u2f_registrations"`
}
const
maxU2FRegistrations
=
20
// Validate the request.
func
(
r
*
UpdateUserRequest
)
Validate
(
rctx
*
RequestContext
)
error
{
if
len
(
r
.
U2FRegistrations
)
>
maxU2FRegistrations
{
if
r
.
SetU2FRegistrations
&&
len
(
r
.
U2FRegistrations
)
>
maxU2FRegistrations
{
return
newValidationError
(
nil
,
"u2f_registrations"
,
"too many U2F registrations"
)
}
// TODO: better validation of the language code!
if
len
(
r
.
Lang
)
>
2
{
if
r
.
SetLang
&&
(
r
.
Lang
==
""
||
len
(
r
.
Lang
)
>
2
)
{
return
newValidationError
(
nil
,
"lang"
,
"invalid language code"
)
}
...
...
@@ -427,16 +433,22 @@ func (r *UpdateUserRequest) Validate(rctx *RequestContext) error {
// Serve the request.
func
(
r
*
UpdateUserRequest
)
Serve
(
rctx
*
RequestContext
)
(
interface
{},
error
)
{
if
r
.
Lang
!=
""
{
rctx
.
User
.
Lang
=
r
.
Lang
if
r
.
Set
Lang
{
rctx
.
User
.
Lang
=
strings
.
ToLower
(
r
.
Lang
)
}
// TODO: check if setU2FRegistration calls tx.UpdateUser, this is a bug otherwise.
return
nil
,
rctx
.
User
.
setU2FRegistrations
(
rctx
.
Context
,
rctx
.
TX
,
r
.
U2FRegistrations
)
if
r
.
SetU2FRegistrations
{
rctx
.
User
.
U2FRegistrations
=
r
.
U2FRegistrations
if
err
:=
rctx
.
User
.
check2FAState
(
rctx
.
Context
,
rctx
.
TX
);
err
!=
nil
{
return
nil
,
err
}
}
return
nil
,
rctx
.
TX
.
UpdateUser
(
rctx
.
Context
,
&
rctx
.
User
.
User
)
}
// AdminUpdateUserRequest is the privileged version of UpdateUser and
// allows to update
many more
attributes. It is a catch-all function
// allows to update
privileged
attributes. It is a catch-all function
// for very simple changes that don't justify their own specialized
// method.
type
AdminUpdateUserRequest
struct
{
...
...
backend/cache/cache.go
View file @
21a62aa5
...
...
@@ -166,7 +166,7 @@ func (c *cacheTX) SetUserEncryptionPublicKey(ctx context.Context, user *as.User,
return
err
}
func
(
c
*
cacheTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
as
.
User
,
asp
*
as
.
AppSpecificPassword
Info
,
pw
string
)
error
{
func
(
c
*
cacheTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
as
.
User
,
asp
*
ct
.
AppSpecificPassword
,
pw
string
)
error
{
err
:=
c
.
TX
.
SetApplicationSpecificPassword
(
ctx
,
user
,
asp
,
pw
)
if
err
==
nil
{
c
.
invalidateUser
(
user
.
Name
)
...
...
backend/instrumented/instrumented_backend.go
View file @
21a62aa5
...
...
@@ -110,7 +110,7 @@ func (tx instrumentedTX) SetUserEncryptionPublicKey(ctx context.Context, u *as.U
return
tx
.
TX
.
SetUserEncryptionPublicKey
(
ctx
,
u
,
k
)
}
func
(
tx
instrumentedTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
u
*
as
.
User
,
a
*
as
.
AppSpecificPassword
Info
,
s
string
)
error
{
func
(
tx
instrumentedTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
u
*
as
.
User
,
a
*
ct
.
AppSpecificPassword
,
s
string
)
error
{
counters
.
WithLabelValues
(
tx
.
name
,
"SetApplicationSpecificPassword"
)
.
Inc
()
return
tx
.
TX
.
SetApplicationSpecificPassword
(
ctx
,
u
,
a
,
s
)
}
...
...
backend/ldap/composite_values.go
View file @
21a62aa5
package
ldapbackend
import
(
"encoding/base64"
as
"git.autistici.org/ai3/accountserver"
ct
"git.autistici.org/ai3/go-common/ldap/compositetypes"
)
...
...
@@ -13,15 +10,6 @@ import (
// is an overall better strategy than crashing, but it's a bit gentler
// on the user.
func
newAppSpecificPassword
(
info
as
.
AppSpecificPasswordInfo
,
pw
string
)
*
ct
.
AppSpecificPassword
{
return
&
ct
.
AppSpecificPassword
{
ID
:
info
.
ID
,
Service
:
info
.
Service
,
Comment
:
info
.
Comment
,
EncryptedPassword
:
pw
,
}
}
func
decodeAppSpecificPasswords
(
values
[]
string
)
[]
*
ct
.
AppSpecificPassword
{
var
out
[]
*
ct
.
AppSpecificPassword
for
_
,
value
:=
range
values
{
...
...
@@ -50,16 +38,11 @@ func encodeAppSpecificPasswords(asps []*ct.AppSpecificPassword) []string {
return
out
}
func
getASPInfo
(
asps
[]
*
ct
.
AppSpecificPassword
)
[]
*
as
.
AppSpecificPasswordInfo
{
var
out
[]
*
as
.
AppSpecificPasswordInfo
for
_
,
asp
:=
range
asps
{
out
=
append
(
out
,
&
as
.
AppSpecificPasswordInfo
{
ID
:
asp
.
ID
,
Service
:
asp
.
Service
,
Comment
:
asp
.
Comment
,
})
func
sanitizeAppSpecificPasswords
(
asps
[]
*
ct
.
AppSpecificPassword
)
[]
*
ct
.
AppSpecificPassword
{
for
i
:=
0
;
i
<
len
(
asps
);
i
++
{
asps
[
i
]
.
EncryptedPassword
=
""
}
return
out
return
asps
}
func
decodeUserEncryptionKeys
(
values
[]
string
)
[]
*
ct
.
EncryptedKey
{
...
...
@@ -82,41 +65,20 @@ func encodeUserEncryptionKeys(keys []*ct.EncryptedKey) []string {
return
out
}
func
decodeU2FRegistrations
(
encRegs
[]
string
)
[]
*
as
.
U2FRegistration
{
var
out
[]
*
as
.
U2FRegistration
func
decodeU2FRegistrations
(
encRegs
[]
string
)
[]
*
ct
.
U2FRegistration
{
var
out
[]
*
ct
.
U2FRegistration
for
_
,
enc
:=
range
encRegs
{
if
r
,
err
:=
ct
.
UnmarshalU2FRegistration
(
enc
);
err
==
nil
{
// Convert ct.U2FRegistration (internal) ->
// as.U2FRegistration (public) by
// base64-encoding the data.
out
=
append
(
out
,
&
as
.
U2FRegistration
{
KeyHandle
:
base64
.
StdEncoding
.
EncodeToString
(
r
.
KeyHandle
),
PublicKey
:
base64
.
StdEncoding
.
EncodeToString
(
r
.
PublicKey
),
})
out
=
append
(
out
,
r
)
}
}
return
out
}
func
encodeU2FRegistrations
(
regs
[]
*
as
.
U2FRegistration
)
[]
string
{
func
encodeU2FRegistrations
(
regs
[]
*
ct
.
U2FRegistration
)
[]
string
{
var
out
[]
string
for
_
,
r
:=
range
regs
{
// Convert as.U2FRegistration (public) ->
// ct.U2FRegistration (internal) by base64-decoding
// the data.
kh
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
r
.
KeyHandle
)
if
err
!=
nil
{
continue
}
pk
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
r
.
PublicKey
)
if
err
!=
nil
{
continue
}
ctr
:=
ct
.
U2FRegistration
{
KeyHandle
:
kh
,
PublicKey
:
pk
,
}
out
=
append
(
out
,
ctr
.
Marshal
())
out
=
append
(
out
,
r
.
Marshal
())
}
return
out
}
backend/ldap/model.go
View file @
21a62aa5
...
...
@@ -144,7 +144,7 @@ func newUser(entry *ldap.Entry) (*as.RawUser, error) {
LastPasswordChangeStamp
:
decodeShadowTimestamp
(
entry
.
GetAttributeValue
(
passwordLastChangeLDAPAttr
)),
AccountRecoveryHint
:
entry
.
GetAttributeValue
(
recoveryHintLDAPAttr
),
U2FRegistrations
:
decodeU2FRegistrations
(
entry
.
GetAttributeValues
(
u2fRegistrationsLDAPAttr
)),
AppSpecificPasswords
:
getASPInfo
(
decodeAppSpecificPasswords
(
entry
.
GetAttributeValues
(
aspLDAPAttr
))),
AppSpecificPasswords
:
sanitizeAppSpecificPasswords
(
decodeAppSpecificPasswords
(
entry
.
GetAttributeValues
(
aspLDAPAttr
))),
HasOTP
:
entry
.
GetAttributeValue
(
totpSecretLDAPAttr
)
!=
""
,
},
// Remove the legacy LDAP {crypt} prefix on old passwords.
...
...
@@ -425,10 +425,20 @@ func (tx *backendTX) SetUserEncryptionPublicKey(ctx context.Context, user *as.Us
return
nil
}
func
(
tx
*
backendTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
as
.
User
,
info
*
as
.
AppSpecificPassword
Info
,
encryptedPassword
string
)
error
{
func
(
tx
*
backendTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
as
.
User
,
asp
*
ct
.
AppSpecificPassword
,
encryptedPassword
string
)
error
{
dn
:=
tx
.
getUserDN
(
user
)
asps
:=
decodeAppSpecificPasswords
(
tx
.
readAttributeValues
(
ctx
,
dn
,
aspLDAPAttr
))
asps
=
append
(
excludeASPFromList
(
asps
,
info
.
ID
),
newAppSpecificPassword
(
*
info
,
encryptedPassword
))
// Build a new AppSpecificPassword that includes the encrypted
// pw, without modifying the original object.
newASP
:=
ct
.
AppSpecificPassword
{
ID
:
asp
.
ID
,
Service
:
asp
.
Service
,
Comment
:
asp
.
Comment
,
EncryptedPassword
:
encryptedPassword
,
}
asps
=
append
(
excludeASPFromList
(
asps
,
asp
.
ID
),
&
newASP
)
outASPs
:=
encodeAppSpecificPasswords
(
asps
)
tx
.
setAttr
(
dn
,
aspLDAPAttr
,
outASPs
...
)
return
nil
...
...
backend/ldap/model_test.go
View file @
21a62aa5
package
ldapbackend
import
(
"bytes"
"context"
"fmt"
"testing"
...
...
@@ -14,35 +15,37 @@ import (
)
const
(
testLDAPPort
=
42871
testLDAPAddr
=
"ldap://127.0.0.1:42871"
testUser1
=
"uno@investici.org"
testUser2
=
"due@investici.org"
// has encryption keys
testUser3
=
"tre@investici.org"
// has OTP
testUser4
=
"quattro@investici.org"
// has mailing lists
testBaseDN
=
"dc=example,dc=com"
testUser1
=
"uno@investici.org"
testUser2
=
"due@investici.org"
// has encryption keys
testUser3
=
"tre@investici.org"
// has OTP
testUser4
=
"quattro@investici.org"
// has mailing lists
testBaseDN
=
"dc=example,dc=com"
)
func
startServerAndGetUser
(
t
testing
.
T
B
)
(
func
()
,
as
.
Backend
,
*
as
.
RawUser
)
{
func
startServerAndGetUser
(
t
*
testing
.
T
)
(
*
ldaptest
.
TestLDAPServer
,
as
.
Backend
,
*
as
.
RawUser
)
{
return
startServerAndGetUserWithName
(
t
,
testUser1
)
}
func
startServerAndGetUser2
(
t
testing
.
T
B
)
(
func
()
,
as
.
Backend
,
*
as
.
RawUser
)
{
func
startServerAndGetUser2
(
t
*
testing
.
T
)
(
*
ldaptest
.
TestLDAPServer
,
as
.
Backend
,
*
as
.
RawUser
)
{
return
startServerAndGetUserWithName
(
t
,
testUser2
)
}
func
startServerAndGetUser3
(
t
testing
.
T
B
)
(
func
()
,
as
.
Backend
,
*
as
.
RawUser
)
{
func
startServerAndGetUser3
(
t
*
testing
.
T
)
(
*
ldaptest
.
TestLDAPServer
,
as
.
Backend
,
*
as
.
RawUser
)
{
return
startServerAndGetUserWithName
(
t
,
testUser3
)
}
func
startServerAndGetUser4
(
t
testing
.
T
B
)
(
func
()
,
as
.
Backend
,
*
as
.
RawUser
)
{
func
startServerAndGetUser4
(
t
*
testing
.
T
)
(
*
ldaptest
.
TestLDAPServer
,
as
.
Backend
,
*
as
.
RawUser
)
{
return
startServerAndGetUserWithName
(
t
,
testUser4
)
}
func
startServer
(
t
testing
.
TB
)
(
func
(),
as
.
Backend
)
{
stop
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
func
startServer
(
t
*
testing
.
T
)
(
*
ldaptest
.
TestLDAPServer
,
as
.
Backend
)
{
// Tell the test runtime that we can run multiple integration tests in
// parallel. This just happens to be a convenient call site for all
// integration tests.
t
.
Parallel
()
srv
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
Dir
:
"../../ldaptest"
,
Port
:
testLDAPPort
,
Base
:
"dc=example,dc=com"
,
LDIFs
:
[]
string
{
"testdata/base.ldif"
,
...
...
@@ -53,16 +56,16 @@ func startServer(t testing.TB) (func(), as.Backend) {
},
})
b
,
err
:=
NewLDAPBackend
(
testLDAP
Addr
,
"cn=manager,dc=example,dc=com"
,
"password"
,
"dc=example,dc=com"
)
b
,
err
:=
NewLDAPBackend
(
srv
.
Addr
,
"cn=manager,dc=example,dc=com"
,
"password"
,
"dc=example,dc=com"
)
if
err
!=
nil
{
t
.
Fatal
(
"NewLDAPBackend"
,
err
)
}
return
s
top
,
b
return
s
rv
,
b
}
func
startServerAndGetUserWithName
(
t
testing
.
T
B
,
username
string
)
(
func
()
,
as
.
Backend
,
*
as
.
RawUser
)
{
s
top
,
b
:=
startServer
(
t
)
func
startServerAndGetUserWithName
(
t
*
testing
.
T
,
username
string
)
(
*
ldaptest
.
TestLDAPServer
,
as
.
Backend
,
*
as
.
RawUser
)
{
s
rv
,
b
:=
startServer
(
t
)
tx
,
_
:=
b
.
NewTransaction
()
user
,
err
:=
tx
.
GetUser
(
context
.
Background
(),
username
)
...
...
@@ -73,12 +76,12 @@ func startServerAndGetUserWithName(t testing.TB, username string) (func(), as.Ba
t
.
Fatalf
(
"could not find test user %s"
,
username
)
}
return
s
top
,
b
,
user
return
s
rv
,
b
,
user
}
func
TestModel_GetUser_NotFound
(
t
*
testing
.
T
)
{
s
top
,
b
:=
startServer
(
t
)
defer
s
top
()
s
rv
,
b
:=
startServer
(
t
)
defer
s
rv
.
Close
()
tx
,
_
:=
b
.
NewTransaction
()
user
,
err
:=
tx
.
GetUser
(
context
.
Background
(),
"wrong_user"
)
...
...
@@ -91,8 +94,8 @@ func TestModel_GetUser_NotFound(t *testing.T) {
}
func
TestModel_GetUser
(
t
*
testing
.
T
)
{
s
top
,
_
,
user
:=
startServerAndGetUser
(
t
)
defer
s
top
()
s
rv
,
_
,
user
:=
startServerAndGetUser
(
t
)
defer
s
rv
.
Close
()
if
user
.
Name
!=
testUser1
{
t
.
Errorf
(
"bad username: expected %s, got %s"
,
testUser1
,
user
.
Name
)
...
...
@@ -126,8 +129,8 @@ func TestModel_GetUser(t *testing.T) {
}
func
TestModel_GetUser_HasEncryptionKeys
(
t
*
testing
.
T
)
{
s
top
,
_
,
user
:=
startServerAndGetUser2
(
t
)
defer
s
top
()
s
rv
,
_
,
user
:=
startServerAndGetUser2
(
t
)
defer
s
rv
.
Close
()
if
!
user
.
HasEncryptionKeys
{
t
.
Errorf
(
"user %s does not appear to have encryption keys"
,
user
.
Name
)
...
...
@@ -135,17 +138,35 @@ func TestModel_GetUser_HasEncryptionKeys(t *testing.T) {
}
func
TestModel_GetUser_Has2FA
(
t
*
testing
.
T
)
{
s
top
,
_
,
user
:=
startServerAndGetUser3
(
t
)
defer
s
top
()
s
rv
,
_
,
user
:=
startServerAndGetUser3
(
t
)
defer
s
rv
.
Close
()
if
!
user
.
Has2FA
{
t
.
Errorf
(
"user %s does not appear to have 2FA enabled"
,
user
.
Name
)
}
}
func
TestModel_GetUser_HasU2FRegistrations
(
t
*
testing
.
T
)
{
srv
,
_
,
user
:=
startServerAndGetUser4
(
t
)
defer
srv
.
Close
()
if
n
:=
len
(
user
.
U2FRegistrations
);
n
!=
2
{
t
.
Errorf
(
"user %s has %d u2f registrations, expected 2"
,
user
.
Name
,
n
)
}
expectedKey
:=
[]
byte
{
164
,
1
,
2
,
3
,
38
,
33
,
88
,
32
,
182
,
233
,
26
,
63
,
41
,
208
,
70
,
136
,
89
,
102
,
192
,
232
,
56
,
134
,
225
,
180
,
18
,
196
,
51
,
198
,
91
,
162
,
121
,
83
,
86
,
85
,
224
,
46
,
64
,
151
,
99
,
8
,
34
,
88
,
32
,
34
,
176
,
200
,
116
,
202
,
44
,
231
,
42
,
170
,
189
,
102
,
70
,
10
,
9
,
116
,
206
,
125
,
3
,
130
,
59
,
200
,
44
,
245
,
249
,
90
,
172
,
181
,
184
,
201
,
81
,
174
,
182
,
}
if
!
bytes
.
Equal
(
user
.
U2FRegistrations
[
0
]
.
PublicKey
,
expectedKey
)
{
t
.
Errorf
(
"user %s has wrong public key for u2f registration: %v"
,
user
.
Name
,
user
.
U2FRegistrations
[
0
]
.
PublicKey
)
}
}
func
TestModel_GetUser_Resources
(
t
*
testing
.
T
)
{
s
top
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
top
()
s
rv
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
rv
.
Close
()
// Ensure that the user *has* resources.
if
len
(
user
.
Resources
)
<
1
{
...
...
@@ -176,8 +197,8 @@ func TestModel_GetUser_Resources(t *testing.T) {
}
func
TestModel_GetUser_MailingListsAndNewsletters
(
t
*
testing
.
T
)
{
s
top
,
_
,
user
:=
startServerAndGetUser4
(
t
)
defer
s
top
()
s
rv
,
_
,
user
:=
startServerAndGetUser4
(
t
)
defer
s
rv
.
Close
()
// Ensure that the user has the expected number of list resources.
// The backend should find two lists, one of which has an alias as the owner.
...
...
@@ -194,8 +215,8 @@ func TestModel_GetUser_MailingListsAndNewsletters(t *testing.T) {
}
func
TestModel_SearchUser
(
t
*
testing
.
T
)
{
s
top
,
b
:=
startServer
(
t
)
defer
s
top
()
s
rv
,
b
:=
startServer
(
t
)
defer
s
rv
.
Close
()
tx
,
_
:=
b
.
NewTransaction
()
users
,
err
:=
tx
.
SearchUser
(
context
.
Background
(),
"uno"
,
0
)
if
err
!=
nil
{
...
...
@@ -210,15 +231,16 @@ func TestModel_SearchUser(t *testing.T) {
}
func
TestModel_SetResourceStatus
(
t
*
testing
.
T
)
{
stop
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
t
.
Parallel
()
srv
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
Dir
:
"../../ldaptest"
,
Port
:
testLDAPPort
,
Base
:
"dc=example,dc=com"
,
LDIFs
:
[]
string
{
"testdata/base.ldif"
,
"testdata/test1.ldif"
},
})
defer
s
top
()
defer
s
rv
.
Close
()
b
,
err
:=
NewLDAPBackend
(
testLDAP
Addr
,
"cn=manager,dc=example,dc=com"
,
"password"
,
"dc=example,dc=com"
)
b
,
err
:=
NewLDAPBackend
(
srv
.
Addr
,
"cn=manager,dc=example,dc=com"
,
"password"
,
"dc=example,dc=com"
)
if
err
!=
nil
{
t
.
Fatal
(
"NewLDAPBackend"
,
err
)
}
...
...
@@ -243,15 +265,16 @@ func TestModel_SetResourceStatus(t *testing.T) {
}
func
TestModel_HasAnyResource
(
t
*
testing
.
T
)
{
stop
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
t
.
Parallel
()
srv
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
Dir
:
"../../ldaptest"
,
Port
:
testLDAPPort
,
Base
:
"dc=example,dc=com"
,
LDIFs
:
[]
string
{
"testdata/base.ldif"
,
"testdata/test1.ldif"
},
})
defer
s
top
()
defer
s
rv
.
Close
()
b
,
err
:=
NewLDAPBackend
(
testLDAP
Addr
,
"cn=manager,dc=example,dc=com"
,
"password"
,
"dc=example,dc=com"
)
b
,
err
:=
NewLDAPBackend
(
srv
.
Addr
,
"cn=manager,dc=example,dc=com"
,
"password"
,
"dc=example,dc=com"
)
if
err
!=
nil
{
t
.
Fatal
(
"NewLDAPBackend"
,
err
)
}
...
...
@@ -283,8 +306,8 @@ func TestModel_HasAnyResource(t *testing.T) {
}
func
TestModel_SearchResource
(
t
*
testing
.
T
)
{
s
top
,
b
:=
startServer
(
t
)
defer
s
top
()
s
rv
,
b
:=
startServer
(
t
)
defer
s
rv
.
Close
()
for
_
,
pattern
:=
range
[]
string
{
"uno@investici.org"
,
"uno*"
}
{
tx
,
_
:=
b
.
NewTransaction
()
...
...
@@ -302,8 +325,8 @@ func TestModel_SearchResource(t *testing.T) {
}
func
TestModel_SetUserPassword
(
t
*
testing
.
T
)
{
s
top
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
top
()
s
rv
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
rv
.
Close
()
encPass
:=
"encrypted password"
...
...
@@ -333,8 +356,8 @@ func TestModel_SetUserPassword(t *testing.T) {
}
func
TestModel_SetUserEncryptionKeys_Add
(
t
*
testing
.
T
)
{
s
top
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
top
()
s
rv
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
rv
.
Close
()
tx
,
_
:=
b
.
NewTransaction
()
keys
:=
[]
*
ct
.
EncryptedKey
{
...
...
@@ -352,8 +375,8 @@ func TestModel_SetUserEncryptionKeys_Add(t *testing.T) {
}
func
TestModel_SetUserEncryptionKeys_Replace
(
t
*
testing
.
T
)
{
s
top
,
b
,
user
:=
startServerAndGetUser2
(
t
)
defer
s
top
()
s
rv
,
b
,
user
:=
startServerAndGetUser2
(
t
)
defer
s
rv
.
Close
()
tx
,
_
:=
b
.
NewTransaction
()
keys
:=
[]
*
ct
.
EncryptedKey
{
...
...
@@ -371,8 +394,8 @@ func TestModel_SetUserEncryptionKeys_Replace(t *testing.T) {
}
func
TestModel_NextUID
(
t
*
testing
.
T
)
{
s
top
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
top
()
s
rv
,
b
,
user
:=
startServerAndGetUser
(
t
)
defer
s
rv
.
Close
()
tx
,
_
:=
b
.
NewTransaction
()
// User UID should not be available.
...
...
@@ -408,6 +431,8 @@ func TestModel_NextUID(t *testing.T) {
}
func
TestSortResources
(
t
*
testing
.
T
)
{
t
.
Parallel
()
rsrcs
:=
[]
*
as
.
Resource
{
&
as
.
Resource
{
ID
:
"id1"
,
...
...
@@ -437,6 +462,8 @@ func TestSortResources(t *testing.T) {
}
func
TestSortResources_ExternalParentID
(
t
*
testing
.
T
)
{
t
.
Parallel
()
rsrcs
:=
[]
*
as
.
Resource
{
&
as
.
Resource
{
ID
:
"id1"
,
...
...
backend/ldap/resources.go
View file @
21a62aa5
...
...
@@ -100,7 +100,8 @@ func setCommonResourceAttrs(entry *ldap.Entry, rsrc *as.Resource) {
func
(
reg
*
resourceRegistry
)
FromLDAP
(
entry
*
ldap
.
Entry
)
(
rsrc
*
as
.
Resource
,
err
error
)
{
// Since we don't know what resource type to expect, we try
// all known handlers until one returns a valid Resource.
// This is slightly dangerous unless all
// This expects that all object types can be told apart by
// their DN or attributes.
for
_
,
h
:=
range
reg
.
handlers
{
rsrc
,
err
=
h
.
FromLDAP
(
entry
)
if
err
==
nil
{
...
...