Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
ai3
accountserver
Commits
813221d2
Commit
813221d2
authored
Nov 01, 2018
by
ale
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use a shorter import name for the top-level accountserver package
parent
50f6eaa4
Pipeline
#1447
passed with stages
in 2 minutes and 22 seconds
Changes
5
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
181 additions
and
159 deletions
+181
-159
backend/composite_values.go
backend/composite_values.go
+10
-10
backend/model.go
backend/model.go
+78
-56
backend/model_test.go
backend/model_test.go
+25
-25
backend/resources.go
backend/resources.go
+63
-63
backend/resources_test.go
backend/resources_test.go
+5
-5
No files found.
backend/composite_values.go
View file @
813221d2
...
...
@@ -5,13 +5,13 @@ import (
"fmt"
"strings"
"git.autistici.org/ai3/accountserver"
as
"git.autistici.org/ai3/accountserver"
)
// Extend the AppSpecificPasswordInfo type, which only contains public
// information, with the encrypted password.
type
appSpecificPassword
struct
{
a
ccountserver
.
AppSpecificPasswordInfo
a
s
.
AppSpecificPasswordInfo
Password
string
}
...
...
@@ -23,7 +23,7 @@ func (p *appSpecificPassword) Encode() string {
},
":"
)
}
func
newAppSpecificPassword
(
info
a
ccountserver
.
AppSpecificPasswordInfo
,
pw
string
)
*
appSpecificPassword
{
func
newAppSpecificPassword
(
info
a
s
.
AppSpecificPasswordInfo
,
pw
string
)
*
appSpecificPassword
{
return
&
appSpecificPassword
{
AppSpecificPasswordInfo
:
info
,
Password
:
pw
,
...
...
@@ -35,7 +35,7 @@ func parseAppSpecificPassword(asp string) (*appSpecificPassword, error) {
if
len
(
parts
)
!=
3
{
return
nil
,
errors
.
New
(
"badly encoded app-specific password"
)
}
return
newAppSpecificPassword
(
a
ccountserver
.
AppSpecificPasswordInfo
{
return
newAppSpecificPassword
(
a
s
.
AppSpecificPasswordInfo
{
Service
:
parts
[
0
],
Comment
:
parts
[
2
],
},
parts
[
1
]),
nil
...
...
@@ -59,22 +59,22 @@ func encodeAppSpecificPasswords(asps []*appSpecificPassword) []string {
return
out
}
func
getASPInfo
(
asps
[]
*
appSpecificPassword
)
[]
*
a
ccountserver
.
AppSpecificPasswordInfo
{
var
out
[]
*
a
ccountserver
.
AppSpecificPasswordInfo
func
getASPInfo
(
asps
[]
*
appSpecificPassword
)
[]
*
a
s
.
AppSpecificPasswordInfo
{
var
out
[]
*
a
s
.
AppSpecificPasswordInfo
for
_
,
asp
:=
range
asps
{
out
=
append
(
out
,
&
asp
.
AppSpecificPasswordInfo
)
}
return
out
}
func
decodeUserEncryptionKeys
(
values
[]
string
)
[]
*
a
ccountserver
.
UserEncryptionKey
{
var
out
[]
*
a
ccountserver
.
UserEncryptionKey
func
decodeUserEncryptionKeys
(
values
[]
string
)
[]
*
a
s
.
UserEncryptionKey
{
var
out
[]
*
a
s
.
UserEncryptionKey
for
_
,
value
:=
range
values
{
idx
:=
strings
.
IndexByte
(
value
,
':'
)
if
idx
<
0
{
continue
}
out
=
append
(
out
,
&
a
ccountserver
.
UserEncryptionKey
{
out
=
append
(
out
,
&
a
s
.
UserEncryptionKey
{
ID
:
value
[
:
idx
],
Key
:
[]
byte
(
value
[
idx
+
1
:
]),
})
...
...
@@ -82,7 +82,7 @@ func decodeUserEncryptionKeys(values []string) []*accountserver.UserEncryptionKe
return
out
}
func
encodeUserEncryptionKeys
(
keys
[]
*
a
ccountserver
.
UserEncryptionKey
)
[]
string
{
func
encodeUserEncryptionKeys
(
keys
[]
*
a
s
.
UserEncryptionKey
)
[]
string
{
var
out
[]
string
for
_
,
key
:=
range
keys
{
out
=
append
(
out
,
fmt
.
Sprintf
(
"%s:%s"
,
key
.
ID
,
string
(
key
.
Key
)))
...
...
backend/model.go
View file @
813221d2
...
...
@@ -11,7 +11,7 @@ import (
"github.com/tstranex/u2f"
"gopkg.in/ldap.v2"
"git.autistici.org/ai3/accountserver"
as
"git.autistici.org/ai3/accountserver"
)
const
(
...
...
@@ -59,7 +59,7 @@ type backendTX struct {
const
ldapPoolSize
=
20
func
(
b
*
backend
)
NewTransaction
()
(
a
ccountserver
.
TX
,
error
)
{
func
(
b
*
backend
)
NewTransaction
()
(
a
s
.
TX
,
error
)
{
return
&
backendTX
{
ldapTX
:
newLDAPTX
(
b
.
conn
),
backend
:
b
,
...
...
@@ -68,7 +68,7 @@ func (b *backend) NewTransaction() (accountserver.TX, error) {
// NewLDAPBackend initializes an LDAPBackend object with the given LDAP
// connection pool.
func
NewLDAPBackend
(
uri
,
bindDN
,
bindPw
,
base
string
)
(
a
ccountserver
.
Backend
,
error
)
{
func
NewLDAPBackend
(
uri
,
bindDN
,
bindPw
,
base
string
)
(
a
s
.
Backend
,
error
)
{
pool
,
err
:=
ldaputil
.
NewConnectionPool
(
uri
,
bindDN
,
bindPw
,
ldapPoolSize
)
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -78,12 +78,12 @@ func NewLDAPBackend(uri, bindDN, bindPw, base string) (accountserver.Backend, er
func
newLDAPBackendWithConn
(
conn
ldapConn
,
base
string
)
(
*
backend
,
error
)
{
rsrc
:=
newResourceRegistry
()
rsrc
.
register
(
a
ccountserver
.
ResourceTypeEmail
,
&
emailResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
ccountserver
.
ResourceTypeMailingList
,
&
mailingListResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
ccountserver
.
ResourceTypeDAV
,
&
webdavResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
ccountserver
.
ResourceTypeWebsite
,
&
websiteResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
ccountserver
.
ResourceTypeDomain
,
&
domainResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
ccountserver
.
ResourceTypeDatabase
,
&
databaseResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
s
.
ResourceTypeEmail
,
&
emailResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
s
.
ResourceTypeMailingList
,
&
mailingListResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
s
.
ResourceTypeDAV
,
&
webdavResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
s
.
ResourceTypeWebsite
,
&
websiteResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
s
.
ResourceTypeDomain
,
&
domainResourceHandler
{
baseDN
:
base
})
rsrc
.
register
(
a
s
.
ResourceTypeDatabase
,
&
databaseResourceHandler
{
baseDN
:
base
})
return
&
backend
{
conn
:
conn
,
...
...
@@ -112,7 +112,7 @@ func newLDAPBackendWithConn(conn ldapConn, base string) (*backend, error) {
},
nil
}
func
newUser
(
entry
*
ldap
.
Entry
)
(
*
a
ccountserver
.
User
,
error
)
{
func
newUser
(
entry
*
ldap
.
Entry
)
(
*
a
s
.
User
,
error
)
{
// Note that some user-level attributes related to
// authentication are stored on the uid= object, while others
// are on the email= object. We set the latter in the GetUser
...
...
@@ -121,8 +121,8 @@ func newUser(entry *ldap.Entry) (*accountserver.User, error) {
// The case of password recovery attributes is more complex:
// the current schema has those on email=, but we'd like to
// move them to uid=, so we currently have to support both.
uidNumber
,
_
:=
strconv
.
Atoi
(
entry
.
GetAttributeValue
(
uidNumberLDAPAttr
))
user
:=
&
a
ccountserver
.
User
{
uidNumber
,
_
:=
strconv
.
Atoi
(
entry
.
GetAttributeValue
(
uidNumberLDAPAttr
))
// nolint
user
:=
&
a
s
.
User
{
Name
:
entry
.
GetAttributeValue
(
"uid"
),
Lang
:
entry
.
GetAttributeValue
(
preferredLanguageLDAPAttr
),
UID
:
uidNumber
,
...
...
@@ -140,7 +140,7 @@ func newUser(entry *ldap.Entry) (*accountserver.User, error) {
return
user
,
nil
}
func
userToLDAP
(
user
*
a
ccountserver
.
User
)
(
attrs
[]
ldap
.
PartialAttribute
)
{
func
userToLDAP
(
user
*
a
s
.
User
)
(
attrs
[]
ldap
.
PartialAttribute
)
{
// Most attributes are read-only and have specialized methods to set them.
attrs
=
append
(
attrs
,
[]
ldap
.
PartialAttribute
{
{
Type
:
"objectClass"
,
Vals
:
[]
string
{
"top"
,
"person"
,
"posixAccount"
,
"shadowAccount"
,
"organizationalPerson"
,
"inetOrgPerson"
,
"totpAccount"
}},
...
...
@@ -161,21 +161,22 @@ func userToLDAP(user *accountserver.User) (attrs []ldap.PartialAttribute) {
return
}
func
decodeU2FRegistration
(
enc
string
)
(
*
u2f
.
Registration
,
error
)
{
func
decodeU2FRegistration
(
enc
string
)
(
*
as
.
U2F
Registration
,
error
)
{
var
reg
u2f
.
Registration
if
err
:=
reg
.
UnmarshalBinary
([]
byte
(
enc
));
err
!=
nil
{
return
nil
,
err
}
return
&
reg
,
nil
return
&
as
.
U2FRegistration
{
Registration
:
&
reg
}
,
nil
}
func
encodeU2FRegistration
(
r
*
u2f
.
Registration
)
string
{
b
,
_
:=
r
.
MarshalBinary
()
func
encodeU2FRegistration
(
r
*
as
.
U2FRegistration
)
string
{
// MarshalBinary can't fail, ignore error.
b
,
_
:=
r
.
MarshalBinary
()
// nolint
return
string
(
b
)
}
func
decodeU2FRegistrations
(
encRegs
[]
string
)
[]
*
u2f
.
Registration
{
var
out
[]
*
u2f
.
Registration
func
decodeU2FRegistrations
(
encRegs
[]
string
)
[]
*
as
.
U2F
Registration
{
var
out
[]
*
as
.
U2F
Registration
for
_
,
enc
:=
range
encRegs
{
if
r
,
err
:=
decodeU2FRegistration
(
enc
);
err
==
nil
{
out
=
append
(
out
,
r
)
...
...
@@ -184,7 +185,7 @@ func decodeU2FRegistrations(encRegs []string) []*u2f.Registration {
return
out
}
func
encodeU2FRegistrations
(
regs
[]
*
u2f
.
Registration
)
[]
string
{
func
encodeU2FRegistrations
(
regs
[]
*
as
.
U2F
Registration
)
[]
string
{
var
out
[]
string
for
_
,
r
:=
range
regs
{
out
=
append
(
out
,
encodeU2FRegistration
(
r
))
...
...
@@ -192,12 +193,12 @@ func encodeU2FRegistrations(regs []*u2f.Registration) []string {
return
out
}
func
(
tx
*
backendTX
)
getUserDN
(
user
*
a
ccountserver
.
User
)
string
{
func
(
tx
*
backendTX
)
getUserDN
(
user
*
a
s
.
User
)
string
{
return
joinDN
(
"uid="
+
user
.
Name
,
"ou=People"
,
tx
.
backend
.
baseDN
)
}
// CreateUser creates a new user.
func
(
tx
*
backendTX
)
CreateUser
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
)
error
{
func
(
tx
*
backendTX
)
CreateUser
(
ctx
context
.
Context
,
user
*
a
s
.
User
)
error
{
dn
:=
tx
.
getUserDN
(
user
)
tx
.
create
(
dn
)
...
...
@@ -216,7 +217,7 @@ func (tx *backendTX) CreateUser(ctx context.Context, user *accountserver.User) e
}
// UpdateUser updates values for the user only (not the resources).
func
(
tx
*
backendTX
)
UpdateUser
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
)
error
{
func
(
tx
*
backendTX
)
UpdateUser
(
ctx
context
.
Context
,
user
*
a
s
.
User
)
error
{
dn
:=
tx
.
getUserDN
(
user
)
for
_
,
attr
:=
range
userToLDAP
(
user
)
{
tx
.
setAttr
(
dn
,
attr
.
Type
,
attr
.
Vals
...
)
...
...
@@ -225,7 +226,7 @@ func (tx *backendTX) UpdateUser(ctx context.Context, user *accountserver.User) e
}
// GetUser returns a user.
func
(
tx
*
backendTX
)
GetUser
(
ctx
context
.
Context
,
username
string
)
(
*
a
ccountserver
.
User
,
error
)
{
func
(
tx
*
backendTX
)
GetUser
(
ctx
context
.
Context
,
username
string
)
(
*
a
s
.
User
,
error
)
{
// First of all, find the main user object, and just that one.
vars
:=
map
[
string
]
string
{
"user"
:
username
}
result
,
err
:=
tx
.
search
(
ctx
,
tx
.
backend
.
userQuery
.
query
(
vars
))
...
...
@@ -276,17 +277,20 @@ func (tx *backendTX) GetUser(ctx context.Context, username string) (*accountserv
return
user
,
nil
}
func
(
tx
*
backendTX
)
SetUserPassword
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
,
encryptedPassword
string
)
error
{
func
(
tx
*
backendTX
)
SetUserPassword
(
ctx
context
.
Context
,
user
*
a
s
.
User
,
encryptedPassword
string
)
(
err
error
)
{
dn
:=
tx
.
getUserDN
(
user
)
tx
.
setAttr
(
dn
,
passwordLDAPAttr
,
encryptedPassword
)
for
_
,
r
:=
range
user
.
GetResourcesByType
(
accountserver
.
ResourceTypeEmail
)
{
dn
,
_
=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
for
_
,
r
:=
range
user
.
GetResourcesByType
(
as
.
ResourceTypeEmail
)
{
dn
,
err
=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
}
tx
.
setAttr
(
dn
,
passwordLDAPAttr
,
encryptedPassword
)
}
return
nil
return
}
func
(
tx
*
backendTX
)
GetUserEncryptedPassword
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
)
string
{
func
(
tx
*
backendTX
)
GetUserEncryptedPassword
(
ctx
context
.
Context
,
user
*
a
s
.
User
)
string
{
values
:=
tx
.
readAttributeValues
(
ctx
,
tx
.
getUserDN
(
user
),
passwordLDAPAttr
)
if
len
(
values
)
>
0
{
// Remove legacy LDAP encryption prefix.
...
...
@@ -295,7 +299,7 @@ func (tx *backendTX) GetUserEncryptedPassword(ctx context.Context, user *account
return
""
}
func
(
tx
*
backendTX
)
GetUserRecoveryEncryptedPassword
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
)
string
{
func
(
tx
*
backendTX
)
GetUserRecoveryEncryptedPassword
(
ctx
context
.
Context
,
user
*
a
s
.
User
)
string
{
values
:=
tx
.
readAttributeValues
(
ctx
,
tx
.
getUserDN
(
user
),
recoveryResponseLDAPAttr
)
if
len
(
values
)
>
0
{
// Remove legacy LDAP encryption prefix.
...
...
@@ -304,7 +308,7 @@ func (tx *backendTX) GetUserRecoveryEncryptedPassword(ctx context.Context, user
return
""
}
func
(
tx
*
backendTX
)
SetPasswordRecoveryHint
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
,
hint
,
response
string
)
error
{
func
(
tx
*
backendTX
)
SetPasswordRecoveryHint
(
ctx
context
.
Context
,
user
*
a
s
.
User
,
hint
,
response
string
)
error
{
// Write the password recovery attributes on the uid= object,
// as per the new schema.
dn
:=
tx
.
getUserDN
(
user
)
...
...
@@ -313,25 +317,34 @@ func (tx *backendTX) SetPasswordRecoveryHint(ctx context.Context, user *accounts
return
nil
}
func
(
tx
*
backendTX
)
GetUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
accountserver
.
User
)
([]
*
accountserver
.
UserEncryptionKey
,
error
)
{
r
:=
user
.
GetSingleResourceByType
(
accountserver
.
ResourceTypeEmail
)
dn
,
_
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
func
(
tx
*
backendTX
)
GetUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
as
.
User
)
([]
*
as
.
UserEncryptionKey
,
error
)
{
r
:=
user
.
GetSingleResourceByType
(
as
.
ResourceTypeEmail
)
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
nil
,
err
}
rawKeys
:=
tx
.
readAttributeValues
(
ctx
,
dn
,
storagePrivateKeyLDAPAttr
)
return
decodeUserEncryptionKeys
(
rawKeys
),
nil
}
func
(
tx
*
backendTX
)
SetUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
,
keys
[]
*
a
ccountserver
.
UserEncryptionKey
)
error
{
func
(
tx
*
backendTX
)
SetUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
a
s
.
User
,
keys
[]
*
a
s
.
UserEncryptionKey
)
error
{
encKeys
:=
encodeUserEncryptionKeys
(
keys
)
for
_
,
r
:=
range
user
.
GetResourcesByType
(
accountserver
.
ResourceTypeEmail
)
{
dn
,
_
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
for
_
,
r
:=
range
user
.
GetResourcesByType
(
as
.
ResourceTypeEmail
)
{
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
err
}
tx
.
setAttr
(
dn
,
storagePrivateKeyLDAPAttr
,
encKeys
...
)
}
return
nil
}
func
(
tx
*
backendTX
)
SetUserEncryptionPublicKey
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
pub
[]
byte
)
error
{
for
_
,
r
:=
range
user
.
GetResourcesByType
(
accountserver
.
ResourceTypeEmail
)
{
dn
,
_
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
func
(
tx
*
backendTX
)
SetUserEncryptionPublicKey
(
ctx
context
.
Context
,
user
*
as
.
User
,
pub
[]
byte
)
error
{
for
_
,
r
:=
range
user
.
GetResourcesByType
(
as
.
ResourceTypeEmail
)
{
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
err
}
tx
.
setAttr
(
dn
,
storagePublicKeyLDAPAttr
,
string
(
pub
))
}
return
nil
...
...
@@ -347,8 +360,11 @@ func excludeASPFromList(asps []*appSpecificPassword, id string) []*appSpecificPa
return
out
}
func
(
tx
*
backendTX
)
setASPOnResource
(
ctx
context
.
Context
,
r
*
accountserver
.
Resource
,
info
*
accountserver
.
AppSpecificPasswordInfo
,
encryptedPassword
string
)
{
dn
,
_
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
func
(
tx
*
backendTX
)
setASPOnResource
(
ctx
context
.
Context
,
r
*
as
.
Resource
,
info
*
as
.
AppSpecificPasswordInfo
,
encryptedPassword
string
)
{
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
}
// Obtain the full list of ASPs from the backend and replace/append the new one.
asps
:=
decodeAppSpecificPasswords
(
tx
.
readAttributeValues
(
ctx
,
dn
,
aspLDAPAttr
))
...
...
@@ -357,40 +373,46 @@ func (tx *backendTX) setASPOnResource(ctx context.Context, r *accountserver.Reso
tx
.
setAttr
(
dn
,
aspLDAPAttr
,
outASPs
...
)
}
func
(
tx
*
backendTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
,
info
*
a
ccountserver
.
AppSpecificPasswordInfo
,
encryptedPassword
string
)
error
{
for
_
,
r
:=
range
user
.
GetResourcesByType
(
a
ccountserver
.
ResourceTypeEmail
)
{
func
(
tx
*
backendTX
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
a
s
.
User
,
info
*
a
s
.
AppSpecificPasswordInfo
,
encryptedPassword
string
)
error
{
for
_
,
r
:=
range
user
.
GetResourcesByType
(
a
s
.
ResourceTypeEmail
)
{
tx
.
setASPOnResource
(
ctx
,
r
,
info
,
encryptedPassword
)
}
return
nil
}
func
(
tx
*
backendTX
)
deleteASPOnResource
(
ctx
context
.
Context
,
r
*
accountserver
.
Resource
,
id
string
)
{
dn
,
_
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
func
(
tx
*
backendTX
)
deleteASPOnResource
(
ctx
context
.
Context
,
r
*
as
.
Resource
,
id
string
)
{
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
}
asps
:=
decodeAppSpecificPasswords
(
tx
.
readAttributeValues
(
ctx
,
dn
,
aspLDAPAttr
))
asps
=
excludeASPFromList
(
asps
,
id
)
outASPs
:=
encodeAppSpecificPasswords
(
asps
)
tx
.
setAttr
(
dn
,
aspLDAPAttr
,
outASPs
...
)
}
func
(
tx
*
backendTX
)
DeleteApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
,
id
string
)
error
{
for
_
,
r
:=
range
user
.
GetResourcesByType
(
a
ccountserver
.
ResourceTypeEmail
)
{
func
(
tx
*
backendTX
)
DeleteApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
a
s
.
User
,
id
string
)
error
{
for
_
,
r
:=
range
user
.
GetResourcesByType
(
a
s
.
ResourceTypeEmail
)
{
tx
.
deleteASPOnResource
(
ctx
,
r
,
id
)
}
return
nil
}
func
(
tx
*
backendTX
)
SetUserTOTPSecret
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
,
secret
string
)
error
{
func
(
tx
*
backendTX
)
SetUserTOTPSecret
(
ctx
context
.
Context
,
user
*
a
s
.
User
,
secret
string
)
error
{
tx
.
setAttr
(
tx
.
getUserDN
(
user
),
totpSecretLDAPAttr
,
secret
)
return
nil
}
func
(
tx
*
backendTX
)
DeleteUserTOTPSecret
(
ctx
context
.
Context
,
user
*
a
ccountserver
.
User
)
error
{
func
(
tx
*
backendTX
)
DeleteUserTOTPSecret
(
ctx
context
.
Context
,
user
*
a
s
.
User
)
error
{
tx
.
setAttr
(
tx
.
getUserDN
(
user
),
totpSecretLDAPAttr
)
return
nil
}
func
(
tx
*
backendTX
)
SetResourcePassword
(
ctx
context
.
Context
,
r
*
accountserver
.
Resource
,
encryptedPassword
string
)
error
{
dn
,
_
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
func
(
tx
*
backendTX
)
SetResourcePassword
(
ctx
context
.
Context
,
r
*
as
.
Resource
,
encryptedPassword
string
)
error
{
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
err
}
tx
.
setAttr
(
dn
,
passwordLDAPAttr
,
encryptedPassword
)
return
nil
}
...
...
@@ -420,7 +442,7 @@ func (tx *backendTX) hasResource(ctx context.Context, resourceType, resourceName
}
// HasAnyResource returns true if any of the specified resources exists.
func
(
tx
*
backendTX
)
HasAnyResource
(
ctx
context
.
Context
,
resourceIDs
[]
a
ccountserver
.
FindResourceRequest
)
(
bool
,
error
)
{
func
(
tx
*
backendTX
)
HasAnyResource
(
ctx
context
.
Context
,
resourceIDs
[]
a
s
.
FindResourceRequest
)
(
bool
,
error
)
{
for
_
,
req
:=
range
resourceIDs
{
has
,
err
:=
tx
.
hasResource
(
ctx
,
req
.
Type
,
req
.
Name
)
if
err
!=
nil
||
has
{
...
...
@@ -431,7 +453,7 @@ func (tx *backendTX) HasAnyResource(ctx context.Context, resourceIDs []accountse
}
// GetResource returns a ResourceWrapper, as part of a read-modify-update transaction.
func
(
tx
*
backendTX
)
GetResource
(
ctx
context
.
Context
,
rsrcID
a
ccountserver
.
ResourceID
)
(
*
a
ccountserver
.
Resource
,
error
)
{
func
(
tx
*
backendTX
)
GetResource
(
ctx
context
.
Context
,
rsrcID
a
s
.
ResourceID
)
(
*
a
s
.
Resource
,
error
)
{
// From the resource ID we can obtain the DN, and fetch it
// straight from LDAP without even doing a real search.
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
rsrcID
)
...
...
@@ -465,7 +487,7 @@ func (tx *backendTX) GetResource(ctx context.Context, rsrcID accountserver.Resou
}
// CreateResource creates a new LDAP-backed resource object.
func
(
tx
*
backendTX
)
CreateResource
(
ctx
context
.
Context
,
r
*
a
ccountserver
.
Resource
)
error
{
func
(
tx
*
backendTX
)
CreateResource
(
ctx
context
.
Context
,
r
*
a
s
.
Resource
)
error
{
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
err
...
...
@@ -480,7 +502,7 @@ func (tx *backendTX) CreateResource(ctx context.Context, r *accountserver.Resour
}
// UpdateResource updates a LDAP-backed resource that was obtained by a previous GetResource call.
func
(
tx
*
backendTX
)
UpdateResource
(
ctx
context
.
Context
,
r
*
a
ccountserver
.
Resource
)
error
{
func
(
tx
*
backendTX
)
UpdateResource
(
ctx
context
.
Context
,
r
*
a
s
.
Resource
)
error
{
dn
,
err
:=
tx
.
backend
.
resources
.
GetDN
(
r
.
ID
)
if
err
!=
nil
{
return
err
...
...
backend/model_test.go
View file @
813221d2
...
...
@@ -6,7 +6,7 @@ import (
"github.com/go-test/deep"
"git.autistici.org/ai3/accountserver"
as
"git.autistici.org/ai3/accountserver"
"git.autistici.org/ai3/accountserver/ldaptest"
)
...
...
@@ -17,15 +17,15 @@ const (
testUser2
=
"due@investici.org"
)
func
startServerAndGetUser
(
t
testing
.
TB
)
(
func
(),
a
ccountserver
.
Backend
,
*
accountserver
.
User
)
{
func
startServerAndGetUser
(
t
testing
.
TB
)
(
func
(),
a
s
.
Backend
,
*
as
.
User
)
{
return
startServerAndGetUserWithName
(
t
,
testUser1
)
}
func
startServerAndGetUser2
(
t
testing
.
TB
)
(
func
(),
a
ccountserver
.
Backend
,
*
accountserver
.
User
)
{
func
startServerAndGetUser2
(
t
testing
.
TB
)
(
func
(),
a
s
.
Backend
,
*
as
.
User
)
{
return
startServerAndGetUserWithName
(
t
,
testUser2
)
}
func
startServer
(
t
testing
.
TB
)
(
func
(),
a
ccountserver
.
Backend
)
{
func
startServer
(
t
testing
.
TB
)
(
func
(),
a
s
.
Backend
)
{
stop
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
Dir
:
"../ldaptest"
,
Port
:
testLDAPPort
,
...
...
@@ -45,7 +45,7 @@ func startServer(t testing.TB) (func(), accountserver.Backend) {
return
stop
,
b
}
func
startServerAndGetUserWithName
(
t
testing
.
TB
,
username
string
)
(
func
(),
a
ccountserver
.
Backend
,
*
accountserver
.
User
)
{
func
startServerAndGetUserWithName
(
t
testing
.
TB
,
username
string
)
(
func
(),
a
s
.
Backend
,
*
as
.
User
)
{
stop
,
b
:=
startServer
(
t
)
tx
,
_
:=
b
.
NewTransaction
()
...
...
@@ -86,25 +86,25 @@ func TestModel_GetUser(t *testing.T) {
}
// Test a specific resource (the database).
db
:=
user
.
GetSingleResourceByType
(
a
ccountserver
.
ResourceTypeDatabase
)
expectedDB
:=
&
a
ccountserver
.
Resource
{
ID
:
a
ccountserver
.
NewResourceID
(
a
ccountserver
.
ResourceTypeDatabase
,
db
:=
user
.
GetSingleResourceByType
(
a
s
.
ResourceTypeDatabase
)
expectedDB
:=
&
a
s
.
Resource
{
ID
:
a
s
.
NewResourceID
(
a
s
.
ResourceTypeDatabase
,
testUser1
,
"alias=uno"
,
"unodb"
,
),
Type
:
a
ccountserver
.
ResourceTypeDatabase
,
ParentID
:
a
ccountserver
.
NewResourceID
(
a
ccountserver
.
ResourceTypeWebsite
,
Type
:
a
s
.
ResourceTypeDatabase
,
ParentID
:
a
s
.
NewResourceID
(
a
s
.
ResourceTypeWebsite
,
testUser1
,
"uno"
,
),
Name
:
"unodb"
,
Shard
:
"host2"
,
OriginalShard
:
"host2"
,
Status
:
a
ccountserver
.
ResourceStatusActive
,
Database
:
&
a
ccountserver
.
Database
{
Status
:
a
s
.
ResourceStatusActive
,
Database
:
&
a
s
.
Database
{
CleartextPassword
:
"password"
,
DBUser
:
"unodb"
,
},
...
...
@@ -168,7 +168,7 @@ func TestModel_SetResourceStatus(t *testing.T) {
}
tx
,
_
:=
b
.
NewTransaction
()
rsrcID
:=
a
ccountserver
.
NewResourceID
(
a
ccountserver
.
ResourceTypeEmail
,
testUser1
,
testUser1
)
rsrcID
:=
a
s
.
NewResourceID
(
a
s
.
ResourceTypeEmail
,
testUser1
,
testUser1
)
r
,
err
:=
tx
.
GetResource
(
context
.
Background
(),
rsrcID
)
if
err
!=
nil
{
t
.
Fatal
(
"GetResource"
,
err
)
...
...
@@ -177,7 +177,7 @@ func TestModel_SetResourceStatus(t *testing.T) {
t
.
Fatalf
(
"could not find test resource %s"
,
rsrcID
)
}
r
.
Status
=
a
ccountserver
.
ResourceStatusInactive
r
.
Status
=
a
s
.
ResourceStatusInactive
if
err
:=
tx
.
UpdateResource
(
context
.
Background
(),
r
);
err
!=
nil
{
t
.
Fatal
(
"UpdateResource"
,
err
)
}
...
...
@@ -203,9 +203,9 @@ func TestModel_HasAnyResource(t *testing.T) {
tx
,
_
:=
b
.
NewTransaction
()
// Request that should succeed.
ok
,
err
:=
tx
.
HasAnyResource
(
context
.
Background
(),
[]
a
ccountserver
.
FindResourceRequest
{
{
Type
:
a
ccountserver
.
ResourceTypeEmail
,
Name
:
"foo"
},
{
Type
:
a
ccountserver
.
ResourceTypeEmail
,
Name
:
testUser1
},
ok
,
err
:=
tx
.
HasAnyResource
(
context
.
Background
(),
[]
a
s
.
FindResourceRequest
{
{
Type
:
a
s
.
ResourceTypeEmail
,
Name
:
"foo"
},
{
Type
:
a
s
.
ResourceTypeEmail
,
Name
:
testUser1
},
})
if
err
!=
nil
{
t
.
Fatal
(
"HasAnyResource"
,
err
)
...
...
@@ -215,8 +215,8 @@ func TestModel_HasAnyResource(t *testing.T) {
}
// Request that should fail (bad resource type).
ok
,
err
=
tx
.
HasAnyResource
(
context
.
Background
(),
[]
a
ccountserver
.
FindResourceRequest
{
{
Type
:
a
ccountserver
.
ResourceTypeDatabase
,
Name
:
testUser1
},
ok
,
err
=
tx
.
HasAnyResource
(
context
.
Background
(),
[]
a
s
.
FindResourceRequest
{
{
Type
:
a
s
.
ResourceTypeDatabase
,
Name
:
testUser1
},
})
if
err
!=
nil
{
t
.
Fatal
(
"HasAnyResource"
,
err
)
...
...
@@ -262,9 +262,9 @@ func TestModel_SetUserEncryptionKeys_Add(t *testing.T) {
defer
stop
()
tx
,
_
:=
b
.
NewTransaction
()
keys
:=
[]
*
a
ccountserver
.
UserEncryptionKey
{
keys
:=
[]
*
a
s
.
UserEncryptionKey
{
{
ID
:
a
ccountserver
.
UserEncryptionKeyMainID
,
ID
:
a
s
.
UserEncryptionKeyMainID
,
Key
:
[]
byte
(
"very secret key"
),
},
}
...
...
@@ -281,9 +281,9 @@ func TestModel_SetUserEncryptionKeys_Replace(t *testing.T) {
defer
stop
()
tx
,
_
:=
b
.
NewTransaction
()
keys
:=
[]
*
a
ccountserver
.
UserEncryptionKey
{
keys
:=
[]
*
a
s
.
UserEncryptionKey
{
{
ID
:
a
ccountserver
.
UserEncryptionKeyMainID
,
ID
:
a
s
.
UserEncryptionKeyMainID
,
Key
:
[]
byte
(
"very secret key"
),
},
}
...
...
backend/resources.go
View file @
813221d2
...
...
@@ -8,15 +8,15 @@ import (
"gopkg.in/ldap.v2"
"git.autistici.org/ai3/accountserver"
as
"git.autistici.org/ai3/accountserver"
)
// Generic resource handler interface. One for each resource type,
// mapping to exactly one LDAP object type.
type
resourceHandler
interface
{
GetDN
(
a
ccountserver
.
ResourceID
)
(
string
,
error
)
ToLDAP
(
*
a
ccountserver
.
Resource
)
[]
ldap
.
PartialAttribute
FromLDAP
(
*
ldap
.
Entry
)
(
*
a
ccountserver
.
Resource
,
error
)
GetDN
(
a
s
.
ResourceID
)
(
string
,
error
)
ToLDAP
(
*
a
s
.
Resource
)
[]
ldap
.
PartialAttribute
FromLDAP
(
*
ldap
.
Entry
)
(
*
a
s
.
Resource
,
error
)
SearchQuery
()
*
queryTemplate
}
...
...
@@ -47,7 +47,7 @@ func (reg *resourceRegistry) dispatch(rsrcType string, f func(resourceHandler) e
return
f
(
h
)
}
func
(
reg
*
resourceRegistry
)
GetDN
(
id
a
ccountserver
.
ResourceID
)
(
s
string
,
err
error
)
{
func
(
reg
*
resourceRegistry
)
GetDN
(
id
a
s
.
ResourceID
)
(
s
string
,
err
error
)
{
err
=
reg
.
dispatch
(
id
.
Type
(),
func
(
h
resourceHandler
)
(
herr
error
)
{
s
,
herr
=
h
.
GetDN
(
id
)
return
...
...
@@ -55,7 +55,7 @@ func (reg *resourceRegistry) GetDN(id accountserver.ResourceID) (s string, err e
return
}
func
(
reg
*
resourceRegistry
)
ToLDAP
(
rsrc
*
a
ccountserver
.
Resource
)
(
attrs
[]
ldap
.
PartialAttribute
)
{
func
(
reg
*
resourceRegistry
)
ToLDAP
(
rsrc
*
a
s
.
Resource
)
(
attrs
[]
ldap
.
PartialAttribute
)
{
if
err
:=
reg
.
dispatch
(
rsrc
.
ID
.
Type
(),
func
(
h
resourceHandler
)
error
{
attrs
=
h
.
ToLDAP
(
rsrc
)
return
nil
...
...
@@ -71,13 +71,13 @@ func (reg *resourceRegistry) ToLDAP(rsrc *accountserver.Resource) (attrs []ldap.
return
}
func
setCommonResourceAttrs
(
entry
*
ldap
.
Entry
,
rsrc
*
a
ccountserver
.
Resource
)
{
func
setCommonResourceAttrs
(
entry
*
ldap
.
Entry
,
rsrc
*
a
s
.
Resource
)
{
rsrc
.
Status
=
entry
.
GetAttributeValue
(
"status"
)
rsrc
.
Shard
=
entry
.
GetAttributeValue
(
"host"
)
rsrc
.
OriginalShard
=
entry
.
GetAttributeValue
(
"originalHost"
)
}
func
(
reg
*
resourceRegistry
)
FromLDAP
(
entry
*
ldap
.
Entry
)
(
rsrc
*
a
ccountserver
.
Resource
,
err
error
)
{
func
(
reg
*
resourceRegistry
)
FromLDAP
(
entry
*
ldap
.
Entry
)
(
rsrc
*
a
s
.
Resource
,
err
error
)
{
// Since we don't know what resource type to expect, we try
// all known handlers until one returns a valid Resource.
for
_
,
h
:=
range
reg
.
handlers
{
...
...
@@ -90,7 +90,7 @@ func (reg *resourceRegistry) FromLDAP(entry *ldap.Entry) (rsrc *accountserver.Re
return
nil
,
errors
.
New
(
"unknown resource"
)
}
func
(
reg
*
resourceRegistry
)
FromLDAPWithType
(
rsrcType
string
,
entry
*
ldap
.
Entry
)
(
rsrc
*
a
ccountserver
.
Resource
,
err
error
)
{
func
(
reg
*
resourceRegistry
)
FromLDAPWithType
(
rsrcType
string
,
entry
*
ldap
.
Entry
)
(
rsrc
*
a
s
.
Resource
,
err
error
)
{
err
=
reg
.
dispatch
(
rsrcType
,
func
(
h
resourceHandler
)
(
rerr
error
)
{
rsrc
,
rerr
=
h
.
FromLDAP
(
entry
)
if
rerr
!=
nil
{
...
...
@@ -131,7 +131,7 @@ type emailResourceHandler struct {
baseDN
string
}
func
(
h
*
emailResourceHandler
)
GetDN
(
id
a
ccountserver
.
ResourceID
)
(
string
,
error
)
{
func
(
h
*
emailResourceHandler
)
GetDN
(
id
a
s
.
ResourceID
)
(
string
,
error
)
{
if
id
.
User
()
==
""
{
return
""
,
errors
.
New
(
"unqualified resource id"
)