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
d8fb3945
Commit
d8fb3945
authored
Apr 02, 2018
by
ale
Browse files
Add methods to enable/disable OTP
parent
ea481352
Changes
4
Hide whitespace changes
Inline
Side-by-side
actions.go
View file @
d8fb3945
...
...
@@ -7,8 +7,9 @@ import (
"errors"
"git.autistici.org/ai3/go-common/pwhash"
sso
"git.autistici.org/id/go-sso"
"git.autistici.org/id/go-sso"
"git.autistici.org/id/keystore/userenckey"
"github.com/pquerna/otp/totp"
)
// Backend user database interface.
...
...
@@ -36,6 +37,8 @@ type Backend interface {
SetUserEncryptionPublicKey
(
context
.
Context
,
*
User
,
[]
byte
)
error
SetApplicationSpecificPassword
(
context
.
Context
,
*
User
,
*
AppSpecificPasswordInfo
,
string
)
error
DeleteApplicationSpecificPassword
(
context
.
Context
,
*
User
,
string
)
error
SetUserTOTPSecret
(
context
.
Context
,
*
User
,
string
)
error
DeleteUserTOTPSecret
(
context
.
Context
,
*
User
)
error
}
// AccountService implements the business logic and high-level
...
...
@@ -313,6 +316,11 @@ func (s *AccountService) CreateApplicationSpecificPassword(ctx context.Context,
return
nil
,
newRequestError
(
err
)
}
// No application-specific passwords unless 2FA is enabled.
if
!
user
.
Has2FA
{
return
nil
,
newRequestError
(
errors
.
New
(
"2FA is not enabled for this user"
))
}
// Create a new application-specific password and set it in
// the database. We don't need to update the User object as
// we're not reusing it.
...
...
@@ -454,6 +462,51 @@ func (s *AccountService) MoveResource(ctx context.Context, req *MoveResourceRequ
return
&
resp
,
nil
}
type
EnableOTPRequest
struct
{
RequestBase
}
type
EnableOTPResponse
struct
{
TOTPSecret
string
`json:"totp_secret"`
}
func
(
s
*
AccountService
)
EnableOTP
(
ctx
context
.
Context
,
req
*
EnableOTPRequest
)
(
*
EnableOTPResponse
,
error
)
{
user
,
err
:=
s
.
authorizeUser
(
ctx
,
req
.
Username
,
req
.
SSO
)
if
err
!=
nil
{
return
nil
,
err
}
// Replace or initialize the TOTP secret.
totpSecret
,
err
:=
generateTOTPSecret
()
if
err
!=
nil
{
return
nil
,
err
}
if
err
:=
s
.
backend
.
SetUserTOTPSecret
(
ctx
,
user
,
totpSecret
);
err
!=
nil
{
return
nil
,
newBackendError
(
err
)
}
return
&
EnableOTPResponse
{
TOTPSecret
:
totpSecret
,
},
nil
}
type
DisableOTPRequest
struct
{
RequestBase
}
func
(
s
*
AccountService
)
DisableOTP
(
ctx
context
.
Context
,
req
*
DisableOTPRequest
)
error
{
user
,
err
:=
s
.
authorizeUser
(
ctx
,
req
.
Username
,
req
.
SSO
)
if
err
!=
nil
{
return
err
}
// Delete the TOTP secret (if present).
if
err
:=
s
.
backend
.
DeleteUserTOTPSecret
(
ctx
,
user
);
err
!=
nil
{
return
newBackendError
(
err
)
}
return
nil
}
const
appSpecificPasswordLen
=
64
func
randomBase64
(
n
int
)
string
{
...
...
@@ -474,3 +527,11 @@ const appSpecificPasswordIDLen = 4
func
randomAppSpecificPasswordID
()
string
{
return
randomBase64
(
appSpecificPasswordIDLen
)
}
func
generateTOTPSecret
()
(
string
,
error
)
{
key
,
err
:=
totp
.
Generate
(
totp
.
GenerateOpts
{})
if
err
!=
nil
{
return
""
,
err
}
return
key
.
Secret
(),
nil
}
actions_test.go
View file @
d8fb3945
...
...
@@ -58,6 +58,14 @@ func (b *fakeBackend) DeleteApplicationSpecificPassword(_ context.Context, user
return
nil
}
func
(
b
*
fakeBackend
)
SetUserTOTPSecret
(
_
context
.
Context
,
user
*
User
,
secret
string
)
error
{
return
nil
}
func
(
b
*
fakeBackend
)
DeleteUserTOTPSecret
(
_
context
.
Context
,
user
*
User
)
error
{
return
nil
}
const
testAdminGroupName
=
"admins"
type
fakeValidator
struct
{
...
...
backend/model.go
View file @
d8fb3945
...
...
@@ -521,6 +521,22 @@ func (b *LDAPBackend) DeleteApplicationSpecificPassword(ctx context.Context, use
return
b
.
conn
.
Modify
(
ctx
,
mod
)
}
func
(
b
*
LDAPBackend
)
SetUserTOTPSecret
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
secret
string
)
error
{
mod
:=
ldap
.
NewModifyRequest
(
getUserDN
(
user
))
if
b
.
readAttributeValue
(
ctx
,
getUserDN
(
user
),
"totpSecret"
)
==
""
{
mod
.
Add
(
"totpSecret"
,
[]
string
{
secret
})
}
else
{
mod
.
Replace
(
"totpSecret"
,
[]
string
{
secret
})
}
return
b
.
conn
.
Modify
(
ctx
,
mod
)
}
func
(
b
*
LDAPBackend
)
DeleteUserTOTPSecret
(
ctx
context
.
Context
,
user
*
accountserver
.
User
)
error
{
mod
:=
ldap
.
NewModifyRequest
(
getUserDN
(
user
))
mod
.
Delete
(
"totpSecret"
,
nil
)
return
b
.
conn
.
Modify
(
ctx
,
mod
)
}
func
(
b
*
LDAPBackend
)
SetResourcePassword
(
ctx
context
.
Context
,
_
string
,
r
*
accountserver
.
Resource
,
encryptedPassword
string
)
error
{
mod
:=
ldap
.
NewModifyRequest
(
getResourceDN
(
r
))
mod
.
Replace
(
"userPassword"
,
[]
string
{
encryptedPassword
})
...
...
server/server.go
View file @
d8fb3945
...
...
@@ -155,10 +155,43 @@ func (s *AccountServer) handleMoveResource(w http.ResponseWriter, r *http.Reques
serverutil
.
EncodeJSONResponse
(
w
,
resp
)
}
func
(
s
*
AccountServer
)
handleEnableOTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
req
as
.
EnableOTPRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
}
resp
,
err
:=
s
.
service
.
EnableOTP
(
r
.
Context
(),
&
req
)
if
err
!=
nil
{
log
.
Printf
(
"EnableOTP(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
}
serverutil
.
EncodeJSONResponse
(
w
,
resp
)
}
func
(
s
*
AccountServer
)
handleDisableOTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
req
as
.
DisableOTPRequest
if
!
serverutil
.
DecodeJSONRequest
(
w
,
r
,
&
req
)
{
return
}
if
err
:=
s
.
service
.
DisableOTP
(
r
.
Context
(),
&
req
);
err
!=
nil
{
log
.
Printf
(
"DisableOTP(%s): error: %v"
,
req
.
Username
,
err
)
http
.
Error
(
w
,
err
.
Error
(),
errToStatus
(
err
))
return
}
serverutil
.
EncodeJSONResponse
(
w
,
emptyResponse
)
}
func
(
s
*
AccountServer
)
Handler
()
http
.
Handler
{
h
:=
http
.
NewServeMux
()
h
.
HandleFunc
(
"/api/user/get"
,
s
.
handleGetUser
)
h
.
HandleFunc
(
"/api/user/change_password"
,
s
.
handleChangeUserPassword
)
h
.
HandleFunc
(
"/api/user/enable_otp"
,
s
.
handleEnableOTP
)
h
.
HandleFunc
(
"/api/user/disable_otp"
,
s
.
handleDisableOTP
)
h
.
HandleFunc
(
"/api/app_specific_password/create"
,
s
.
handleCreateApplicationSpecificPassword
)
h
.
HandleFunc
(
"/api/app_specific_password/delete"
,
s
.
handleDeleteApplicationSpecificPassword
)
h
.
HandleFunc
(
"/api/resource/enable"
,
s
.
handleEnableResource
)
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment