mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 17:23:22 +00:00
Merge branch 'master' into sasha/cert
This commit is contained in:
commit
6f38f4f418
|
@ -454,11 +454,6 @@ type upsertPasswordReq struct {
|
|||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type upsertPasswordResponse struct {
|
||||
OTPURL string `json:"otp_url"`
|
||||
OTPQRCode []byte `json:"otp_qr"`
|
||||
}
|
||||
|
||||
func (s *APIServer) upsertPassword(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
||||
var req *upsertPasswordReq
|
||||
if err := httplib.ReadJSON(r, &req); err != nil {
|
||||
|
@ -466,12 +461,12 @@ func (s *APIServer) upsertPassword(auth ClientI, w http.ResponseWriter, r *http.
|
|||
}
|
||||
|
||||
user := p.ByName("user")
|
||||
otpURL, otpQRCode, err := auth.UpsertPassword(user, []byte(req.Password))
|
||||
err := auth.UpsertPassword(user, []byte(req.Password))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return &upsertPasswordResponse{OTPURL: otpURL, OTPQRCode: otpQRCode}, nil
|
||||
return message(fmt.Sprintf("password for for user %q upserted", user)), nil
|
||||
}
|
||||
|
||||
type upsertUserRawReq struct {
|
||||
|
|
|
@ -38,7 +38,6 @@ import (
|
|||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/kylelemons/godebug/diff"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
. "gopkg.in/check.v1"
|
||||
|
@ -174,9 +173,9 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
|
|||
|
||||
user1, _ := createUserAndRole(s.clt, "user1", []string{"user1"})
|
||||
user2, _ := createUserAndRole(s.clt, "user2", []string{"user2"})
|
||||
_, _, err = s.clt.UpsertPassword(user1.GetName(), []byte("abc1231"))
|
||||
err = s.clt.UpsertPassword(user1.GetName(), []byte("abc1231"))
|
||||
c.Assert(err, IsNil)
|
||||
_, _, err = s.clt.UpsertPassword(user2.GetName(), []byte("abc1232"))
|
||||
err = s.clt.UpsertPassword(user2.GetName(), []byte("abc1232"))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
newChecker, err := NewAccessChecker(s.AccessS, s.WebS)
|
||||
|
@ -221,7 +220,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
|
|||
}
|
||||
|
||||
func (s *APISuite) TestUserCRUD(c *C) {
|
||||
_, _, err := s.clt.UpsertPassword("user1", []byte("some pass"))
|
||||
err := s.clt.UpsertPassword("user1", []byte("some pass"))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
users, err := s.WebS.GetUsers()
|
||||
|
@ -236,45 +235,78 @@ func (s *APISuite) TestUserCRUD(c *C) {
|
|||
c.Assert(len(users), Equals, 0)
|
||||
}
|
||||
|
||||
// TODO(rjones): Can this test be removed? It seems redundant see lib/services/suite/suite.go.
|
||||
func (s *APISuite) TestPasswordCRUD(c *C) {
|
||||
pass := []byte("abc123")
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
err := s.clt.CheckPassword("user1", pass, "123456")
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
otpURL, _, err := s.clt.UpsertPassword("user1", pass)
|
||||
err = s.clt.UpsertPassword("user1", pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.a.UpsertTOTP("user1", otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.clt.CheckPassword("user1", pass, validToken)
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *APISuite) TestOTPCRUD(c *C) {
|
||||
user := "user1"
|
||||
pass := []byte("abc123")
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
// upsert a password and totp secret
|
||||
err := s.clt.UpsertPassword("user1", pass)
|
||||
c.Assert(err, IsNil)
|
||||
err = s.a.UpsertTOTP(user, otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure the otp url we get back is valid url issued to the correct user
|
||||
otpURL, _, err := s.a.GetOTPData(user)
|
||||
c.Assert(err, IsNil)
|
||||
u, err := url.Parse(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/user1")
|
||||
|
||||
// extract otp secret key from url, we will use this to create
|
||||
// tokens to make sure the url (and qr code) the user receives is
|
||||
// the same as the one stored in the backend
|
||||
otpKeyMeta, err := otp.NewKeyFromURL(u.String())
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyMeta.Secret())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// a completely invalid token should return access denied
|
||||
err = s.clt.CheckPassword("user1", pass, "123456")
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
// a invalid token (made 1 minute in the future but from a valid key)
|
||||
// should also return access denied
|
||||
invalidToken, err := totp.GenerateCode(string(otpKey), time.Now().Add(1*time.Minute))
|
||||
invalidToken, err := totp.GenerateCode(otpSecret, time.Now().Add(1*time.Minute))
|
||||
c.Assert(err, IsNil)
|
||||
err = s.WebS.CheckPassword("user1", pass, invalidToken)
|
||||
err = s.clt.CheckPassword("user1", pass, invalidToken)
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
// a valid token (created right now and from a valid key) should return success
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
err = s.WebS.CheckPassword("user1", pass, validToken)
|
||||
|
||||
err = s.clt.CheckPassword("user1", pass, validToken)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// try the same valid token now it should fail because we don't allow re-use of tokens
|
||||
err = s.clt.CheckPassword("user1", pass, validToken)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *APISuite) PasswordGarbage(c *C) {
|
||||
garbage := [][]byte{
|
||||
nil,
|
||||
make([]byte, defaults.MaxPasswordLength+1),
|
||||
make([]byte, defaults.MinPasswordLength-1),
|
||||
}
|
||||
for _, g := range garbage {
|
||||
err := s.clt.CheckPassword("user1", g, "123456")
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *APISuite) TestSessions(c *C) {
|
||||
|
@ -290,7 +322,7 @@ func (s *APISuite) TestSessions(c *C) {
|
|||
c.Assert(err, NotNil)
|
||||
c.Assert(ws, IsNil)
|
||||
|
||||
_, _, err = s.clt.UpsertPassword(user, pass)
|
||||
err = s.clt.UpsertPassword(user, pass)
|
||||
|
||||
ws, err = s.clt.SignIn(user, pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -73,14 +72,9 @@ func (s *AuthSuite) TestSessions(c *C) {
|
|||
|
||||
createUserAndRole(s.a, user, []string{user})
|
||||
|
||||
otpURL, _, err := s.a.UpsertPassword(user, pass)
|
||||
err = s.a.UpsertPassword(user, pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure label in url is correct
|
||||
u, err := url.Parse(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/user1")
|
||||
|
||||
ws, err = s.a.SignIn(user, pass)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(ws, NotNil)
|
||||
|
@ -108,14 +102,9 @@ func (s *AuthSuite) TestUserLock(c *C) {
|
|||
|
||||
createUserAndRole(s.a, user, []string{user})
|
||||
|
||||
otpURL, _, err := s.a.UpsertPassword(user, pass)
|
||||
err = s.a.UpsertPassword(user, pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure label in url is correct
|
||||
u, err := url.Parse(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/user1")
|
||||
|
||||
// successfull log in
|
||||
ws, err = s.a.SignIn(user, pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
|
|
@ -242,18 +242,32 @@ func (a *AuthWithRoles) UpsertToken(token string, roles teleport.Roles, ttl time
|
|||
return a.authServer.UpsertToken(token, roles, ttl)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) UpsertPassword(user string, password []byte) (hotpURL string, hotpQR []byte, err error) {
|
||||
func (a *AuthWithRoles) UpsertPassword(user string, password []byte) error {
|
||||
if err := a.currentUserAction(user); err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return a.authServer.UpsertPassword(user, password)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) CheckPassword(user string, password []byte, hotpToken string) error {
|
||||
func (a *AuthWithRoles) CheckPassword(user string, password []byte, otpToken string) error {
|
||||
if err := a.currentUserAction(user); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return a.authServer.CheckPassword(user, password, hotpToken)
|
||||
return a.authServer.CheckPassword(user, password, otpToken)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) UpsertTOTP(user string, otpSecret string) error {
|
||||
if err := a.currentUserAction(user); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return a.authServer.UpsertTOTP(user, otpSecret)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) GetOTPData(user string) (string, []byte, error) {
|
||||
if err := a.currentUserAction(user); err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
return a.authServer.GetOTPData(user)
|
||||
}
|
||||
|
||||
func (a *AuthWithRoles) SignIn(user string, password []byte) (*Session, error) {
|
||||
|
|
|
@ -515,22 +515,17 @@ func (c *Client) GetU2FAppID() (string, error) {
|
|||
}
|
||||
|
||||
// UpsertPassword updates web access password for the user
|
||||
func (c *Client) UpsertPassword(user string, password []byte) (otpURL string, otpQRCode []byte, err error) {
|
||||
out, err := c.PostJSON(
|
||||
func (c *Client) UpsertPassword(user string, password []byte) error {
|
||||
_, err := c.PostJSON(
|
||||
c.Endpoint("users", user, "web", "password"),
|
||||
upsertPasswordReq{
|
||||
Password: string(password),
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
var re *upsertPasswordResponse
|
||||
if err := json.Unmarshal(out.Bytes(), &re); err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return re.OTPURL, re.OTPQRCode, err
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertUser user updates or inserts user entry
|
||||
|
@ -1172,7 +1167,7 @@ type WebService interface {
|
|||
// IdentityService manages identities and userse
|
||||
type IdentityService interface {
|
||||
// UpsertPassword updates web access password for the user
|
||||
UpsertPassword(user string, password []byte) (otpURL string, otpQRCode []byte, err error)
|
||||
UpsertPassword(user string, password []byte) error
|
||||
|
||||
// UpsertOIDCConnector updates or creates OIDC connector
|
||||
UpsertOIDCConnector(connector services.OIDCConnector, ttl time.Duration) error
|
||||
|
|
|
@ -25,7 +25,6 @@ package auth
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"image/png"
|
||||
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
|
@ -171,30 +170,28 @@ func (s *AuthServer) CreateUserWithToken(token string, password string, otpToken
|
|||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = s.checkAndUpsertTOTP(tokenData.User.Name, otpToken, tokenData.OTPKey)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
_, _, err = s.UpsertPassword(tokenData.User.Name, []byte(password))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// TODO(rjones): We have to do this because UpsertPassword above wipes out the stored OTP secret.
|
||||
// To fix this, we need to update UpsertPassword so it doesn't do that but we need to make sure
|
||||
// that changing the behavior of UpsertPassword doesn't break things elsewhere.
|
||||
err = s.UpsertTOTP(tokenData.User.Name, tokenData.OTPKey)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = s.CheckOTP(tokenData.User.Name, otpToken)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = s.UpsertPassword(tokenData.User.Name, []byte(password))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// apply user allowed logins
|
||||
role := services.RoleForUser(tokenData.User.V2())
|
||||
role.SetLogins(tokenData.User.AllowedLogins)
|
||||
if err := s.UpsertRole(role); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Allowed logins are not going to be used anymore
|
||||
tokenData.User.AllowedLogins = nil
|
||||
tokenData.User.Roles = append(tokenData.User.Roles, role.GetName())
|
||||
|
@ -223,42 +220,6 @@ func (s *AuthServer) CreateUserWithToken(token string, password string, otpToken
|
|||
return sess, nil
|
||||
}
|
||||
|
||||
// checkAndUpsertTOTP validates token and if valid, it upserts hotp key. This function
|
||||
// does not perform a security check but rather a usability check to ensure the
|
||||
// second factor works.
|
||||
func (s *AuthServer) checkAndUpsertTOTP(username string, otpToken string, otpKey string) error {
|
||||
// make sure we have not seen this otp token before
|
||||
usedToken, err := s.GetUsedTOTPToken(username)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare([]byte(otpToken), []byte(usedToken)) == 1 {
|
||||
return trace.AccessDenied("previously used totp token")
|
||||
}
|
||||
|
||||
// totp tokens are only valid during there time window t
|
||||
valid := totp.Validate(otpToken, otpKey)
|
||||
if !valid {
|
||||
return trace.BadParameter("invalid TOTP token")
|
||||
}
|
||||
|
||||
// if we have gotten here we were able to verify the otp token
|
||||
// so go ahead and upsert it into the backend
|
||||
err = s.UpsertTOTP(username, otpKey)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// we successfully validated this otp token, don't let it be used again for some time t
|
||||
err = s.UpsertUsedTOTPToken(username, otpToken)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthServer) CreateUserWithU2FToken(token string, password string, response u2f.RegisterResponse) (*Session, error) {
|
||||
err := s.CheckU2FEnabled()
|
||||
if err != nil {
|
||||
|
@ -290,15 +251,17 @@ func (s *AuthServer) CreateUserWithU2FToken(token string, password string, respo
|
|||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
_, _, err = s.UpsertPassword(tokenData.User.Name, []byte(password))
|
||||
err = s.UpsertPassword(tokenData.User.Name, []byte(password))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
role := services.RoleForUser(tokenData.User.V2())
|
||||
role.SetLogins(tokenData.User.AllowedLogins)
|
||||
if err := s.UpsertRole(role); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Allowed logins are not going to be used anymore
|
||||
tokenData.User.AllowedLogins = nil
|
||||
tokenData.User.Roles = append(tokenData.User.Roles, role.GetName())
|
||||
|
|
142
lib/auth/password.go
Normal file
142
lib/auth/password.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
// CheckPasswordWOToken checks just password without checking OTP tokens
|
||||
// used in case of SSH authentication, when token has been validated.
|
||||
func (s *AuthServer) CheckPasswordWOToken(user string, password []byte) error {
|
||||
err := services.VerifyPassword(password)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
hash, err := s.GetPasswordHash(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
if err = bcrypt.CompareHashAndPassword(hash, password); err != nil {
|
||||
return trace.BadParameter("passwords do not match")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckPassword checks the password and OTP token. Called by tsh or lib/web/*.
|
||||
func (s *AuthServer) CheckPassword(user string, password []byte, otpToken string) error {
|
||||
err := s.CheckPasswordWOToken(user, password)
|
||||
if err != nil {
|
||||
return trace.AccessDenied("invalid password")
|
||||
}
|
||||
|
||||
err = s.CheckOTP(user, otpToken)
|
||||
if err != nil {
|
||||
return trace.AccessDenied("invalid otp token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckOTP determines the type of OTP token used (for legacy HOTP support), fetches the
|
||||
// appropriate type from the backend, and checks if the token is valid.
|
||||
func (s *AuthServer) CheckOTP(user string, otpToken string) error {
|
||||
var err error
|
||||
|
||||
otpType, err := s.getOTPType(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
switch otpType {
|
||||
case "hotp":
|
||||
otp, err := s.GetHOTP(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// look ahead n tokens to see if we can find a matching token
|
||||
if !otp.Scan(otpToken, defaults.HOTPFirstTokensRange) {
|
||||
return trace.AccessDenied("bad htop token")
|
||||
}
|
||||
|
||||
// we need to upsert the hotp state again because the
|
||||
// counter was incremented
|
||||
if err := s.UpsertHOTP(user, otp); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
case "totp":
|
||||
otpSecret, err := s.GetTOTP(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// get the previously used token to mitigate token replay attacks
|
||||
usedToken, err := s.GetUsedTOTPToken(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// we use a constant time compare function to mitigate timing attacks
|
||||
if subtle.ConstantTimeCompare([]byte(otpToken), []byte(usedToken)) == 1 {
|
||||
return trace.AccessDenied("previously used totp token")
|
||||
}
|
||||
|
||||
valid := totp.Validate(otpToken, otpSecret)
|
||||
if !valid {
|
||||
return trace.AccessDenied("invalid totp token")
|
||||
}
|
||||
|
||||
// if we have a valid token, update the previously used token
|
||||
err = s.UpsertUsedTOTPToken(user, otpToken)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOTPType returns the type of OTP token used, HOTP or TOTP.
|
||||
// Deprecated: Remove this method once HOTP support has been removed.
|
||||
func (s *AuthServer) getOTPType(user string) (string, error) {
|
||||
_, err := s.GetHOTP(user)
|
||||
if err != nil {
|
||||
if trace.IsNotFound(err) {
|
||||
return "totp", nil
|
||||
}
|
||||
return "", trace.Wrap(err)
|
||||
}
|
||||
return "hotp", nil
|
||||
}
|
||||
|
||||
// GetOTPData returns the OTP Key, Key URL, and the QR code.
|
||||
func (s *AuthServer) GetOTPData(user string) (string, []byte, error) {
|
||||
// get otp key from backend
|
||||
otpSecret, err := s.GetTOTP(user)
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// create otp url
|
||||
params := map[string][]byte{"secret": []byte(otpSecret)}
|
||||
otpURL := utils.GenerateOTPURL("totp", user, params)
|
||||
|
||||
// create the qr code
|
||||
otpQR, err := utils.GenerateQRCode(otpURL)
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return otpURL, otpQR, nil
|
||||
}
|
|
@ -36,7 +36,6 @@ import (
|
|||
"github.com/gravitational/teleport/lib/sshutils"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
. "gopkg.in/check.v1"
|
||||
|
@ -135,13 +134,21 @@ func (s *TunSuite) TestUnixServerClient(c *C) {
|
|||
|
||||
userName := "test"
|
||||
pass := []byte("pwd123")
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
user, role := createUserAndRole(s.a, userName, []string{userName})
|
||||
role.SetResource(services.KindNode, services.RW())
|
||||
err = s.a.UpsertRole(role)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
otpURL, _, err := s.a.UpsertPassword(user.GetName(), pass)
|
||||
err = s.a.UpsertPassword(user.GetName(), pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.a.UpsertTOTP(user.GetName(), otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
otpURL, _, err := s.a.GetOTPData(userName)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure label in url is correct
|
||||
|
@ -149,14 +156,8 @@ func (s *TunSuite) TestUnixServerClient(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/test")
|
||||
|
||||
// extract otp key from signup url
|
||||
otpKeyMeta, err := otp.NewKeyFromURL(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyMeta.Secret())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// create a valid otp token
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
authMethod, err := NewWebPasswordAuth(user.GetName(), pass, validToken)
|
||||
|
@ -177,10 +178,18 @@ func (s *TunSuite) TestSessions(c *C) {
|
|||
|
||||
user := "ws-test"
|
||||
pass := []byte("ws-abc123")
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
createUserAndRole(s.a, user, []string{user})
|
||||
|
||||
otpURL, _, err := s.a.UpsertPassword(user, pass)
|
||||
err := s.a.UpsertPassword(user, pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.a.UpsertTOTP(user, otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
otpURL, _, err := s.a.GetOTPData(user)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure label in url is correct
|
||||
|
@ -188,14 +197,8 @@ func (s *TunSuite) TestSessions(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/ws-test")
|
||||
|
||||
// extract otp key from signup url
|
||||
otpKeyMeta, err := otp.NewKeyFromURL(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyMeta.Secret())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// create a valid otp token
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
authMethod, err := NewWebPasswordAuth(user, pass, validToken)
|
||||
|
@ -390,10 +393,18 @@ func (s *TunSuite) TestPermissions(c *C) {
|
|||
|
||||
userName := "ws-test2"
|
||||
pass := []byte("ws-abc1234")
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
user, _ := createUserAndRole(s.a, userName, []string{userName})
|
||||
|
||||
otpURL, _, err := s.a.UpsertPassword(user.GetName(), pass)
|
||||
err := s.a.UpsertPassword(user.GetName(), pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.a.UpsertTOTP(user.GetName(), otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
otpURL, _, err := s.a.GetOTPData(userName)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure label in url is correct
|
||||
|
@ -401,14 +412,8 @@ func (s *TunSuite) TestPermissions(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/ws-test2")
|
||||
|
||||
// extract otp key from signup url
|
||||
otpKeyMeta, err := otp.NewKeyFromURL(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyMeta.Secret())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// create a valid otp token
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
authMethod, err := NewWebPasswordAuth(user.GetName(), pass, validToken)
|
||||
|
@ -466,8 +471,16 @@ func (s *TunSuite) TestSessionsBadPassword(c *C) {
|
|||
|
||||
user := "system-test"
|
||||
pass := []byte("system-abc123")
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
otpURL, _, err := s.a.UpsertPassword(user, pass)
|
||||
err := s.a.UpsertPassword(user, pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.a.UpsertTOTP(user, otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
otpURL, _, err := s.a.GetOTPData(user)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure label in url is correct
|
||||
|
@ -475,14 +488,8 @@ func (s *TunSuite) TestSessionsBadPassword(c *C) {
|
|||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/system-test")
|
||||
|
||||
// extract otp key from signup url
|
||||
otpKeyMeta, err := otp.NewKeyFromURL(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyMeta.Secret())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// create a valid otp token
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
authMethod, err := NewWebPasswordAuth(user, pass, validToken)
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
// Package services implements API services exposed by Teleport:
|
||||
// * presence service that takes care of heratbeats
|
||||
// * presence service that takes care of heartbeats
|
||||
// * web service that takes care of web logins
|
||||
// * ca service - certificate authorities
|
||||
package services
|
||||
|
@ -101,14 +101,7 @@ type Identity interface {
|
|||
DeleteWebSession(user, sid string) error
|
||||
|
||||
// UpsertPassword upserts new password and OTP token
|
||||
UpsertPassword(user string, password []byte) (otpURL string, otpQRCode []byte, err error)
|
||||
|
||||
// CheckPassword is called on web user or tsh user login using the password and otp token
|
||||
CheckPassword(user string, password []byte, otpToken string) error
|
||||
|
||||
// CheckPasswordWOToken checks just password without checking HOTP tokens
|
||||
// used in case of SSH authentication, when token has been validated
|
||||
CheckPasswordWOToken(user string, password []byte) error
|
||||
UpsertPassword(user string, password []byte) error
|
||||
|
||||
// UpsertSignupToken upserts signup token - one time token that lets user to create a user account
|
||||
UpsertSignupToken(token string, tokenData SignupToken, ttl time.Duration) error
|
||||
|
|
|
@ -86,14 +86,6 @@ func (s *ServicesSuite) TestPasswordHashCRUD(c *C) {
|
|||
s.suite.PasswordHashCRUD(c)
|
||||
}
|
||||
|
||||
func (s *ServicesSuite) TestPasswordAndHotpCRUD(c *C) {
|
||||
s.suite.PasswordCRUD(c)
|
||||
}
|
||||
|
||||
func (s *ServicesSuite) TestPasswordGarbage(c *C) {
|
||||
s.suite.PasswordGarbage(c)
|
||||
}
|
||||
|
||||
func (s *ServicesSuite) TestWebSessionCRUD(c *C) {
|
||||
s.suite.WebSessionCRUD(c)
|
||||
}
|
||||
|
|
|
@ -17,14 +17,11 @@ limitations under the License.
|
|||
package local
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/subtle"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
|
@ -33,14 +30,12 @@ import (
|
|||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gokyle/hotp"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/tstranex/u2f"
|
||||
)
|
||||
|
||||
|
@ -231,7 +226,7 @@ func (s *IdentityService) GetHOTP(user string) (*hotp.HOTP, error) {
|
|||
return otp, nil
|
||||
}
|
||||
|
||||
// UpsertTOTP upserts TOTP secret key for a user that can be used to generate and validate tokens
|
||||
// UpsertTOTP upserts TOTP secret key for a user that can be used to generate and validate tokens.
|
||||
func (s *IdentityService) UpsertTOTP(user string, secretKey string) error {
|
||||
err := s.backend.UpsertVal([]string{"web", "users", user}, "totp", []byte(secretKey), 0)
|
||||
if err != nil {
|
||||
|
@ -366,128 +361,23 @@ func (s *IdentityService) DeleteWebSession(user, sid string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// UpsertPassword upserts new password and OTP token and returns OTP url and QR code.
|
||||
func (s *IdentityService) UpsertPassword(user string, password []byte) (otpURL string, otpQRCode []byte, err error) {
|
||||
if err := services.VerifyPassword(password); err != nil {
|
||||
return "", nil, err
|
||||
// UpsertPassword upserts new password hash into a backend.
|
||||
func (s *IdentityService) UpsertPassword(user string, password []byte) error {
|
||||
err := services.VerifyPassword(password)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
hash, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = s.UpsertPasswordHash(user, hash)
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
otpURL, otpQRCode, err = s.createAndUpsertTOTP(user)
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return otpURL, otpQRCode, nil
|
||||
}
|
||||
|
||||
// CheckPassword is called on web user or tsh user login using the password and otp token
|
||||
func (s *IdentityService) CheckPassword(user string, password []byte, otpToken string) error {
|
||||
var err error
|
||||
|
||||
hash, err := s.GetPasswordHash(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword(hash, password); err != nil {
|
||||
return trace.AccessDenied("passwords do not match")
|
||||
}
|
||||
|
||||
return s.checkOTP(user, otpToken)
|
||||
}
|
||||
|
||||
// checkOTP determines the type of OTP token used (for legacy HOTP support), fetches the
|
||||
// appropriate type from the backend, and checks the token.
|
||||
func (s *IdentityService) checkOTP(user string, otpToken string) error {
|
||||
var err error
|
||||
|
||||
otpType, err := s.getOTPType(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
switch otpType {
|
||||
case "hotp":
|
||||
otp, err := s.GetHOTP(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// look ahead n tokens to see if we can find a matching token
|
||||
if !otp.Scan(otpToken, defaults.HOTPFirstTokensRange) {
|
||||
return trace.AccessDenied("bad htop token")
|
||||
}
|
||||
|
||||
// we need to upsert the hotp state again because the
|
||||
// counter was incremented
|
||||
if err := s.UpsertHOTP(user, otp); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
case "totp":
|
||||
otpSecret, err := s.GetTOTP(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
usedToken, err := s.GetUsedTOTPToken(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare([]byte(otpToken), []byte(usedToken)) == 1 {
|
||||
return trace.AccessDenied("previously used totp token")
|
||||
}
|
||||
|
||||
valid := totp.Validate(otpToken, otpSecret)
|
||||
if !valid {
|
||||
return trace.AccessDenied("invalid totp token")
|
||||
}
|
||||
|
||||
err = s.UpsertUsedTOTPToken(user, otpToken)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOTPType returns the type of OTP token used, HOTP or TOTP.
|
||||
// Deprecated: Remove this method once HOTP support has been removed.
|
||||
func (s *IdentityService) getOTPType(user string) (string, error) {
|
||||
_, err := s.GetHOTP(user)
|
||||
if err != nil {
|
||||
if trace.IsNotFound(err) {
|
||||
return "totp", nil
|
||||
}
|
||||
return "", trace.Wrap(err)
|
||||
}
|
||||
return "hotp", nil
|
||||
}
|
||||
|
||||
// CheckPasswordWOToken checks just password without checking HOTP tokens
|
||||
// used in case of SSH authentication, when token has been validated
|
||||
func (s *IdentityService) CheckPasswordWOToken(user string, password []byte) error {
|
||||
if err := services.VerifyPassword(password); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
hash, err := s.GetPasswordHash(user)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
if err := bcrypt.CompareHashAndPassword(hash, password); err != nil {
|
||||
return trace.BadParameter("passwords do not match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -786,34 +676,3 @@ func (s *IdentityService) GetOIDCAuthRequest(stateToken string) (*services.OIDCA
|
|||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *IdentityService) createAndUpsertTOTP(user string) (otpURI string, otpQRCode []byte, err error) {
|
||||
// create totp key
|
||||
otpKey, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: "Teleport",
|
||||
AccountName: user,
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// create QR code
|
||||
var otpQRBuf bytes.Buffer
|
||||
otpImage, err := otpKey.Image(456, 456)
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
png.Encode(&otpQRBuf, otpImage)
|
||||
|
||||
// create otp url, only the key is needed
|
||||
otpKeyBytes := []byte(otpKey.Secret())
|
||||
otpURI = utils.GenerateOTPURL("totp", user, map[string][]byte{"secret": otpKeyBytes})
|
||||
|
||||
// upsert totp key into the backend
|
||||
err = s.UpsertTOTP(user, otpKey.Secret())
|
||||
if err != nil {
|
||||
return "", nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return otpURI, otpQRBuf.Bytes(), nil
|
||||
}
|
||||
|
|
|
@ -19,9 +19,7 @@ package suite
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
|
@ -32,8 +30,6 @@ import (
|
|||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/tstranex/u2f"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
|
@ -372,51 +368,6 @@ func (s *ServicesTestSuite) TokenCRUD(c *C) {
|
|||
c.Assert(trace.IsNotFound(err), Equals, true, Commentf("%#v", err))
|
||||
}
|
||||
|
||||
func (s *ServicesTestSuite) PasswordCRUD(c *C) {
|
||||
pass := []byte("abc123")
|
||||
|
||||
err := s.WebS.CheckPassword("user1", pass, "123456")
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
// upsert a password which will create a new source of totp tokens
|
||||
otpURL, _, err := s.WebS.UpsertPassword("user1", pass)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// make sure the otp url we get back is valid url issued to the correct user
|
||||
u, err := url.Parse(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(u.Path, Equals, "/user1")
|
||||
|
||||
// extract otp secret key from url, we will use this to create
|
||||
// tokens to make sure the url (and qr code) the user receives is
|
||||
// the same as the one stored in the backend
|
||||
otpKeyMeta, err := otp.NewKeyFromURL(u.String())
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyMeta.Secret())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// a completely invalid token should return access denied
|
||||
err = s.WebS.CheckPassword("user1", pass, "123456")
|
||||
c.Assert(trace.IsAccessDenied(err), Equals, true, Commentf("%T", err))
|
||||
|
||||
// a invalid token (made 1 minute in the future but from a valid key)
|
||||
// should also return access denied
|
||||
invalidToken, err := totp.GenerateCode(string(otpKey), time.Now().Add(1*time.Minute))
|
||||
c.Assert(err, IsNil)
|
||||
err = s.WebS.CheckPassword("user1", pass, invalidToken)
|
||||
c.Assert(trace.IsAccessDenied(err), Equals, true, Commentf("%T", err))
|
||||
|
||||
// a valid token (created right now and from a valid key) should return success
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
err = s.WebS.CheckPassword("user1", pass, validToken)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// try the same valid token now it should fail because we don't allow re-use of tokens
|
||||
err = s.WebS.CheckPassword("user1", pass, validToken)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *ServicesTestSuite) RolesCRUD(c *C) {
|
||||
out, err := s.Access.GetRoles()
|
||||
c.Assert(err, IsNil)
|
||||
|
@ -483,18 +434,6 @@ func (s *ServicesTestSuite) NamespacesCRUD(c *C) {
|
|||
c.Assert(trace.IsNotFound(err), Equals, true, Commentf("%T", err))
|
||||
}
|
||||
|
||||
func (s *ServicesTestSuite) PasswordGarbage(c *C) {
|
||||
garbage := [][]byte{
|
||||
nil,
|
||||
make([]byte, defaults.MaxPasswordLength+1),
|
||||
make([]byte, defaults.MinPasswordLength-1),
|
||||
}
|
||||
for _, g := range garbage {
|
||||
err := s.WebS.CheckPassword("user1", g, "123456")
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServicesTestSuite) U2FCRUD(c *C) {
|
||||
token := "tok1"
|
||||
appId := "https://localhost"
|
||||
|
|
|
@ -1,10 +1,37 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base32"
|
||||
"image/png"
|
||||
"net/url"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/pquerna/otp"
|
||||
)
|
||||
|
||||
// GenerateQRCode takes in a OTP Key URL and returns a PNG-encoded QR code.
|
||||
func GenerateQRCode(u string) ([]byte, error) {
|
||||
otpKey, err := otp.NewKeyFromURL(u)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
otpImage, err := otpKey.Image(450, 450)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
var otpQR bytes.Buffer
|
||||
err = png.Encode(&otpQR, otpImage)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return otpQR.Bytes(), nil
|
||||
}
|
||||
|
||||
// GenerateOTPURL returns a OTP Key URL that can be used to construct a HOTP or TOTP key. For more
|
||||
// details see: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
// Example: otpauth://totp/foo:bar@baz.com?secret=qux
|
||||
|
|
|
@ -53,7 +53,6 @@ import (
|
|||
"github.com/gokyle/hotp"
|
||||
"github.com/gravitational/roundtrip"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/tstranex/u2f"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
@ -374,6 +373,8 @@ func (s *WebSuite) authPackFromResponse(c *C, re *roundtrip.Response) *authPack
|
|||
func (s *WebSuite) authPack(c *C) *authPack {
|
||||
user := s.user
|
||||
pass := "abc123"
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
teleUser, err := services.NewUser(user)
|
||||
c.Assert(err, IsNil)
|
||||
|
@ -386,17 +387,14 @@ func (s *WebSuite) authPack(c *C) *authPack {
|
|||
err = s.roleAuth.UpsertUser(teleUser)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
otpURL, _, err := s.roleAuth.UpsertPassword(user, []byte(pass))
|
||||
err = s.roleAuth.UpsertPassword(user, []byte(pass))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// extract key from otp url
|
||||
otpKeyMeta, err := otp.NewKeyFromURL(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyMeta.Secret())
|
||||
err = s.roleAuth.UpsertTOTP(user, otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// create a valid otp token
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
clt := s.client()
|
||||
|
@ -488,18 +486,17 @@ func (s *WebSuite) TestWebSessionsRenew(c *C) {
|
|||
func (s *WebSuite) TestWebSessionsBadInput(c *C) {
|
||||
user := "bob"
|
||||
pass := "abc123"
|
||||
rawSecret := "def456"
|
||||
otpSecret := base32.StdEncoding.EncodeToString([]byte(rawSecret))
|
||||
|
||||
otpURL, _, err := s.roleAuth.UpsertPassword(user, []byte(pass))
|
||||
err := s.roleAuth.UpsertPassword(user, []byte(pass))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// extract otp key from url
|
||||
otpKeyObject, err := otp.NewKeyFromURL(otpURL)
|
||||
c.Assert(err, IsNil)
|
||||
otpKey, err := base32.StdEncoding.DecodeString(otpKeyObject.Secret())
|
||||
err = s.roleAuth.UpsertTOTP(user, otpSecret)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// create valid token
|
||||
validToken, err := totp.GenerateCode(string(otpKey), time.Now())
|
||||
validToken, err := totp.GenerateCode(otpSecret, time.Now())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
clt := s.client()
|
||||
|
|
Loading…
Reference in a new issue