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
aef048c2
Commit
aef048c2
authored
Jun 22, 2018
by
ale
Browse files
Add integration tests
Start a full HTTP server, backed by an in-memory LDAP server, and test the API directly.
parent
9a84289a
Changes
6
Hide whitespace changes
Inline
Side-by-side
backend/model.go
View file @
aef048c2
...
...
@@ -3,7 +3,6 @@ package backend
import
(
"context"
"errors"
"fmt"
"os"
"strings"
...
...
@@ -30,6 +29,7 @@ type ldapConn interface {
// all users.
type
backend
struct
{
conn
ldapConn
baseDN
string
userQuery
*
queryConfig
userResourceQueries
[]
*
queryConfig
resources
*
resourceRegistry
...
...
@@ -71,7 +71,8 @@ func newLDAPBackendWithConn(conn ldapConn, base string) (*backend, error) {
rsrc
.
register
(
accountserver
.
ResourceTypeDatabase
,
&
databaseResourceHandler
{
baseDN
:
base
})
return
&
backend
{
conn
:
conn
,
conn
:
conn
,
baseDN
:
base
,
userQuery
:
mustCompileQueryConfig
(
&
queryConfig
{
Base
:
"uid=${user},ou=People,"
+
base
,
Scope
:
"base"
,
...
...
@@ -186,9 +187,8 @@ func newUser(entry *ldap.Entry) (*accountserver.User, error) {
return
user
,
nil
}
func
getUserDN
(
user
*
accountserver
.
User
)
string
{
// TODO: fix this
return
fmt
.
Sprintf
(
"uid=%s,ou=People,"
,
user
.
Name
)
func
(
tx
*
backendTX
)
getUserDN
(
user
*
accountserver
.
User
)
string
{
return
joinDN
(
"uid="
+
user
.
Name
,
"ou=People"
,
tx
.
backend
.
baseDN
)
}
// GetUser returns a user.
...
...
@@ -266,23 +266,23 @@ func (tx *backendTX) readAttributeValues(ctx context.Context, dn, attribute stri
}
func
(
tx
*
backendTX
)
SetUserPassword
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
encryptedPassword
string
)
error
{
tx
.
setAttr
(
getUserDN
(
user
),
"userPassword"
,
encryptedPassword
)
tx
.
setAttr
(
tx
.
getUserDN
(
user
),
"userPassword"
,
encryptedPassword
)
return
nil
}
func
(
tx
*
backendTX
)
GetUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
accountserver
.
User
)
([]
*
accountserver
.
UserEncryptionKey
,
error
)
{
rawKeys
:=
tx
.
readAttributeValues
(
ctx
,
getUserDN
(
user
),
"storageEncryptionKey"
)
rawKeys
:=
tx
.
readAttributeValues
(
ctx
,
tx
.
getUserDN
(
user
),
"storageEncryptionKey"
)
return
decodeUserEncryptionKeys
(
rawKeys
),
nil
}
func
(
tx
*
backendTX
)
SetUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
keys
[]
*
accountserver
.
UserEncryptionKey
)
error
{
encKeys
:=
encodeUserEncryptionKeys
(
keys
)
tx
.
setAttr
(
getUserDN
(
user
),
"storageEncryptionKey"
,
encKeys
...
)
tx
.
setAttr
(
tx
.
getUserDN
(
user
),
"storageEncryptionKey"
,
encKeys
...
)
return
nil
}
func
(
tx
*
backendTX
)
SetUserEncryptionPublicKey
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
pub
[]
byte
)
error
{
tx
.
setAttr
(
getUserDN
(
user
),
"storageEncryptionPublicKey"
,
string
(
pub
))
tx
.
setAttr
(
tx
.
getUserDN
(
user
),
"storageEncryptionPublicKey"
,
string
(
pub
))
return
nil
}
...
...
@@ -328,12 +328,12 @@ func (tx *backendTX) DeleteApplicationSpecificPassword(ctx context.Context, user
}
func
(
tx
*
backendTX
)
SetUserTOTPSecret
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
secret
string
)
error
{
tx
.
setAttr
(
getUserDN
(
user
),
"totpSecret"
,
secret
)
tx
.
setAttr
(
tx
.
getUserDN
(
user
),
"totpSecret"
,
secret
)
return
nil
}
func
(
tx
*
backendTX
)
DeleteUserTOTPSecret
(
ctx
context
.
Context
,
user
*
accountserver
.
User
)
error
{
tx
.
setAttr
(
getUserDN
(
user
),
"totpSecret"
)
tx
.
setAttr
(
tx
.
getUserDN
(
user
),
"totpSecret"
)
return
nil
}
...
...
integrationtest/integration_test.go
0 → 100644
View file @
aef048c2
package
integrationtest
import
(
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"git.autistici.org/ai3/accountserver"
"git.autistici.org/ai3/accountserver/backend"
"git.autistici.org/ai3/accountserver/ldaptest"
"git.autistici.org/ai3/accountserver/server"
sso
"git.autistici.org/id/go-sso"
"golang.org/x/crypto/ed25519"
)
const
(
testLDAPPort
=
42872
testLDAPAddr
=
"ldap://127.0.0.1:42872"
testSSODomain
=
"domain"
testSSOService
=
"accountserver.domain/"
testAdminUser
=
"admin"
testAdminGroup
=
"admins"
)
func
withSSO
(
t
testing
.
TB
)
(
func
(),
sso
.
Signer
,
string
)
{
tmpf
,
err
:=
ioutil
.
TempFile
(
""
,
""
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
pub
,
priv
,
err
:=
ed25519
.
GenerateKey
(
nil
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
tmpf
.
Write
(
pub
)
tmpf
.
Close
()
signer
,
err
:=
sso
.
NewSigner
(
priv
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
return
func
()
{
os
.
Remove
(
tmpf
.
Name
())
},
signer
,
tmpf
.
Name
()
}
type
testClient
struct
{
srvURL
string
signer
sso
.
Signer
}
func
(
c
*
testClient
)
ssoTicket
(
username
string
)
string
{
var
groups
[]
string
if
username
==
testAdminUser
{
groups
=
append
(
groups
,
testAdminGroup
)
}
signed
,
err
:=
c
.
signer
.
Sign
(
sso
.
NewTicket
(
username
,
testSSOService
,
testSSODomain
,
""
,
groups
,
1
*
time
.
Hour
))
if
err
!=
nil
{
panic
(
err
)
}
return
signed
}
func
(
c
*
testClient
)
request
(
uri
string
,
req
,
out
interface
{})
error
{
data
,
_
:=
json
.
Marshal
(
req
)
resp
,
err
:=
http
.
Post
(
c
.
srvURL
+
uri
,
"application/json"
,
bytes
.
NewReader
(
data
))
if
err
!=
nil
{
return
err
}
defer
resp
.
Body
.
Close
()
if
resp
.
StatusCode
!=
200
{
return
fmt
.
Errorf
(
"http status code %d"
,
resp
.
StatusCode
)
}
if
resp
.
Header
.
Get
(
"Content-Type"
)
!=
"application/json"
{
return
fmt
.
Errorf
(
"unexpected content-type %s"
,
resp
.
Header
.
Get
(
"Content-Type"
))
}
data
,
_
=
ioutil
.
ReadAll
(
resp
.
Body
)
log
.
Printf
(
"response:
\n
%s
\n
"
,
string
(
data
))
if
out
==
nil
{
return
nil
}
return
json
.
Unmarshal
(
data
,
out
)
}
func
startService
(
t
testing
.
TB
)
(
func
(),
*
testClient
)
{
stop
:=
ldaptest
.
StartServer
(
t
,
&
ldaptest
.
Config
{
Dir
:
"../ldaptest"
,
Port
:
testLDAPPort
,
Base
:
"dc=example,dc=com"
,
LDIFs
:
[]
string
{
"testdata/base.ldif"
,
"testdata/test1.ldif"
},
})
be
,
err
:=
backend
.
NewLDAPBackend
(
testLDAPAddr
,
"cn=manager,dc=example,dc=com"
,
"password"
,
"dc=example,dc=com"
)
if
err
!=
nil
{
t
.
Fatal
(
"NewLDAPBackend"
,
err
)
}
ssoStop
,
signer
,
ssoPubKeyFile
:=
withSSO
(
t
)
var
svcConfig
accountserver
.
Config
svcConfig
.
SSO
.
PublicKeyFile
=
ssoPubKeyFile
svcConfig
.
SSO
.
Domain
=
testSSODomain
svcConfig
.
SSO
.
Service
=
testSSOService
svcConfig
.
SSO
.
AdminGroup
=
testAdminGroup
svcConfig
.
AvailableDomains
=
map
[
string
][]
string
{
accountserver
.
ResourceTypeEmail
:
[]
string
{
"example.com"
},
}
service
,
err
:=
accountserver
.
NewAccountService
(
be
,
&
svcConfig
)
if
err
!=
nil
{
stop
()
t
.
Fatal
(
"NewAccountService"
,
err
)
}
as
:=
server
.
New
(
service
,
be
)
srv
:=
httptest
.
NewServer
(
as
.
Handler
())
c
:=
&
testClient
{
srvURL
:
srv
.
URL
,
signer
:
signer
,
}
return
func
()
{
stop
()
srv
.
Close
()
ssoStop
()
},
c
}
// Verify that authentication on GetUser works as expected:
// - users can fetch their own data but not other users'
// - admins can read everything.
func
TestIntegration_GetUser_Auth
(
t
*
testing
.
T
)
{
stop
,
c
:=
startService
(
t
)
defer
stop
()
testdata
:=
[]
struct
{
authUser
string
expectedOk
bool
}{
{
"uno@investici.org"
,
true
},
{
"due@investici.org"
,
false
},
{
testAdminUser
,
true
},
}
for
_
,
td
:=
range
testdata
{
var
user
accountserver
.
User
err
:=
c
.
request
(
"/api/user/get"
,
&
accountserver
.
GetUserRequest
{
RequestBase
:
accountserver
.
RequestBase
{
Username
:
"uno@investici.org"
,
SSO
:
c
.
ssoTicket
(
td
.
authUser
),
},
},
&
user
)
if
td
.
expectedOk
&&
err
!=
nil
{
t
.
Errorf
(
"access error for user %s: expected ok, got error: %v"
,
td
.
authUser
,
err
)
}
else
if
!
td
.
expectedOk
&&
err
==
nil
{
t
.
Errorf
(
"access error for user %s: expected error, got ok"
,
td
.
authUser
)
}
}
}
// Verify that authentication on GetResource works as expected:
// - users can fetch their own data but not other users'
// - admins can read everything.
func
TestIntegration_ChangeUserPassword
(
t
*
testing
.
T
)
{
stop
,
c
:=
startService
(
t
)
defer
stop
()
err
:=
c
.
request
(
"/api/user/change_password"
,
&
accountserver
.
ChangeUserPasswordRequest
{
PrivilegedRequestBase
:
accountserver
.
PrivilegedRequestBase
{
RequestBase
:
accountserver
.
RequestBase
{
Username
:
"uno@investici.org"
,
SSO
:
c
.
ssoTicket
(
"uno@investici.org"
),
},
CurPassword
:
"password"
,
},
Password
:
"new_password"
,
},
nil
)
if
err
!=
nil
{
t
.
Fatal
(
"ChangePassword"
,
err
)
}
}
integrationtest/testdata/base.ldif
0 → 100644
View file @
aef048c2
dn: dc=example,dc=com
objectclass: domain
objectclass: top
dc: example
dn: ou=People,dc=example,dc=com
objectclass: top
objectclass: organizationalUnit
ou: People
dn: ou=Lists,dc=example,dc=com
objectclass: top
objectclass: organizationalUnit
ou: Lists
integrationtest/testdata/test1.ldif
0 → 100644
View file @
aef048c2
dn: uid=uno@investici.org,ou=People,dc=example,dc=com
cn: uno@investici.org
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
objectClass: organizationalPerson
objectClass: inetOrgPerson
loginShell: /bin/false
uidNumber: 19475
shadowMax: 99999
gidNumber: 2000
gecos: uno@investici.org
sn: Private
homeDirectory: /var/empty
uid: uno@investici.org
givenName: Private
shadowLastChange: 12345
shadowWarning: 7
preferredLanguage: it
userPassword:: e2NyeXB0fSQ2JG1wVzdTZHZROG52OFJabE8kcFNqbm1hLi9CZDNIWU1hL29sMGZ
FRmZrS3pWRjAxUkkzMnpISEoxNnc2V2xaajFuSFVzMmd4ZXZIVDdoemdELnJ5Lk1BZWM3REptZVZ5
WEtkeDV0QTE=
dn: mail=uno@investici.org,uid=uno@investici.org,ou=People,dc=example,dc=com
status: active
recoverQuestion:: dGkgc2VpIG1haSDDuMOgdHRvIG1hbGUgY2FkZW5kbyBkYSB1biBwYWxhenpv
IGRpIG90dG8gcGlhbmk/
objectClass: top
objectClass: virtualMailUser
userPassword:: e2NyeXB0fSQ2JG1wVzdTZHZROG52OFJabE8kcFNqbm1hLi9CZDNIWU1hL29sMGZ
FRmZrS3pWRjAxUkkzMnpISEoxNnc2V2xaajFuSFVzMmd4ZXZIVDdoemdELnJ5Lk1BZWM3REptZVZ5
WEtkeDV0QTE=
uidNumber: 19475
host: host2
mailAlternateAddress: uno@anche.no
recoverAnswer: {crypt}$1$wtEa4TKB$lxeyenkQ1yfxECn7WVQQ0/
gidNumber: 2000
mail: uno@investici.org
creationDate: 2002-05-07
mailMessageStore: investici.org/uno/
originalHost: host2
dn: ftpname=uno,uid=uno@investici.org,ou=People,dc=example,dc=com
status: active
givenName: Private
cn: uno
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: ftpAccount
loginShell: /bin/false
shadowWarning: 7
uidNumber: 19475
host: host2
shadowMax: 99999
ftpname: uno
gidNumber: 33
gecos: FTP Account for uno@investici.org
sn: Private
homeDirectory: /home/users/investici.org/uno
uid: uno
creationDate: 01-08-2013
shadowLastChange: 12345
originalHost: host2
userPassword:: e2NyeXB0fSQ2JElDYkx1WTI3QWl6bC5FeEgkUDhOZHJ3VEtxZ2UwQUp3QW9oNE1
EYlUxU3EySGtuRkF1cEx2RUI0U28waEw5NWtpZ3dIeXQuQnYxS0J5SFM2MXd6RnZuLnJsMEN4eFpx
RVgzUnVxbDE=
dn: alias=uno,uid=uno@investici.org,ou=People,dc=example,dc=com
status: active
parentSite: autistici.org
objectClass: top
objectClass: subSite
alias: uno
host: host2
documentRoot: /home/users/investici.org/uno/html-uno
creationDate: 01-08-2013
originalHost: host2
statsId: 2191
dn: cn=example.com,uid=uno@investici.org,ou=People,dc=example,dc=com
status: active
acceptMail: true
objectClass: top
objectClass: virtualHost
cn: example.com
host: host2
documentRoot: /home/users/investici.org/uno/html-example.com
creationDate: 02-08-2013
originalHost: host2
statsId: 2192
dn: dbname=unodb,alias=uno,uid=uno@investici.org,ou=People,dc=example,dc=com
status: active
clearPassword: password
objectClass: top
objectClass: dbMysql
dbname: unodb
dbuser: unodb
host: host2
creationDate: 01-08-2013
originalHost: host2
server/server.go
View file @
aef048c2
...
...
@@ -10,7 +10,7 @@ import (
as
"git.autistici.org/ai3/accountserver"
)
type
txHandler
func
(
as
.
TX
,
http
.
ResponseWriter
,
*
http
.
Request
)
error
type
txHandler
func
(
as
.
TX
,
http
.
ResponseWriter
,
*
http
.
Request
)
(
interface
{},
error
)
var
errBadRequest
=
errors
.
New
(
"bad request"
)
...
...
@@ -26,7 +26,7 @@ func New(service *as.AccountService, backend as.Backend) *AccountServer {
}
}
var
emptyResponse
=
map
[
string
]
string
{}
var
emptyResponse
struct
{}
func
errToStatus
(
err
error
)
int
{
switch
{
...
...
@@ -41,168 +41,158 @@ func errToStatus(err error) int {
}
}
func
(
s
*
AccountServer
)
handleGetUser
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleGetUser
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
GetUserRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
user
,
err
:=
s
.
service
.
GetUser
(
r
.
Context
(),
tx
,
&
req
)
if
err
!=
nil
{
log
.
Printf
(
"GetUser(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
user
)
return
nil
return
user
,
nil
}
func
(
s
*
AccountServer
)
handleChangeUserPassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleChangeUserPassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
ChangeUserPasswordRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
if
err
:=
s
.
service
.
ChangeUserPassword
(
r
.
Context
(),
tx
,
&
req
);
err
!=
nil
{
log
.
Printf
(
"ChangeUserPassword(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
emptyResponse
)
return
nil
return
&
emptyResponse
,
nil
}
func
(
s
*
AccountServer
)
handleCreateApplicationSpecificPassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleCreateApplicationSpecificPassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
CreateApplicationSpecificPasswordRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
resp
,
err
:=
s
.
service
.
CreateApplicationSpecificPassword
(
r
.
Context
(),
tx
,
&
req
)
if
err
!=
nil
{
log
.
Printf
(
"CreateApplicationSpecificPassword(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
resp
)
return
nil
return
resp
,
nil
}
func
(
s
*
AccountServer
)
handleDeleteApplicationSpecificPassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleDeleteApplicationSpecificPassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
DeleteApplicationSpecificPasswordRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
if
err
:=
s
.
service
.
DeleteApplicationSpecificPassword
(
r
.
Context
(),
tx
,
&
req
);
err
!=
nil
{
log
.
Printf
(
"DeleteApplicationSpecificPassword(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
emptyResponse
)
return
nil
return
&
emptyResponse
,
nil
}
func
(
s
*
AccountServer
)
handleEnableResource
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleEnableResource
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
EnableResourceRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
if
err
:=
s
.
service
.
EnableResource
(
r
.
Context
(),
tx
,
&
req
);
err
!=
nil
{
log
.
Printf
(
"EnableResource(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
emptyResponse
)
return
nil
return
&
emptyResponse
,
nil
}
func
(
s
*
AccountServer
)
handleDisableResource
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleDisableResource
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
DisableResourceRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
if
err
:=
s
.
service
.
DisableResource
(
r
.
Context
(),
tx
,
&
req
);
err
!=
nil
{
log
.
Printf
(
"DisableResource(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
emptyResponse
)
return
nil
return
&
emptyResponse
,
nil
}
func
(
s
*
AccountServer
)
handleChangeResourcePassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleChangeResourcePassword
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
ChangeResourcePasswordRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
if
err
:=
s
.
service
.
ChangeResourcePassword
(
r
.
Context
(),
tx
,
&
req
);
err
!=
nil
{
log
.
Printf
(
"ChangeResourcePassword(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
emptyResponse
)
return
nil
return
&
emptyResponse
,
nil
}
func
(
s
*
AccountServer
)
handleMoveResource
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleMoveResource
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
MoveResourceRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
resp
,
err
:=
s
.
service
.
MoveResource
(
r
.
Context
(),
tx
,
&
req
)
if
err
!=
nil
{
log
.
Printf
(
"MoveResource(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
resp
)
return
nil
return
resp
,
nil
}
func
(
s
*
AccountServer
)
handleEnableOTP
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleEnableOTP
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
EnableOTPRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
resp
,
err
:=
s
.
service
.
EnableOTP
(
r
.
Context
(),
tx
,
&
req
)
if
err
!=
nil
{
log
.
Printf
(
"EnableOTP(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
err
return
nil
,
err
}
serverutil
.
EncodeJSONResponse
(
w
,
resp
)
return
nil
return
resp
,
nil
}
func
(
s
*
AccountServer
)
handleDisableOTP
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
func
(
s
*
AccountServer
)
handleDisableOTP
(
tx
as
.
TX
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
interface
{},
error
)
{
var
req
as
.
DisableOTPRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
errBadRequest
return
nil
,
errBadRequest
}
if
err
:=
s
.
service
.
DisableOTP
(
r
.
Context
(),
tx
,
&
req
);
err
!=
nil
{
log
.
Printf
(
"DisableOTP(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
e