func loadUsersFromReader(r io.Reader) (users []user.UserWithRemoteIdentities, pwis []user.PasswordInfo, err error) { // Encoding used by the user config file. var configUsers []struct { user.User Password string `json:"password"` RemoteIdentities []user.RemoteIdentity `json:"remoteIdentities"` // The old format stored all user data under the "user" key. // Attempt to detect that, and print an better error. OldUserFields map[string]string `json:"user"` } if err := json.NewDecoder(r).Decode(&configUsers); err != nil { return nil, nil, err } users = make([]user.UserWithRemoteIdentities, len(configUsers)) pwis = make([]user.PasswordInfo, len(configUsers)) for i, u := range configUsers { if u.OldUserFields != nil { return nil, nil, fmt.Errorf("Static user file is using an outdated format. Please refer to example in static/fixtures.") } users[i] = user.UserWithRemoteIdentities{ User: u.User, RemoteIdentities: u.RemoteIdentities, } hashedPassword, err := user.NewPasswordFromPlaintext(u.Password) if err != nil { return nil, nil, err } pwis[i] = user.PasswordInfo{UserID: u.ID, Password: hashedPassword} } return }
// RegisterWithPassword creates a new user with the given name and password. // connID is the connector ID of the ConnectorLocal connector. func (m *UserManager) RegisterWithPassword(email, plaintext, connID string) (string, error) { tx, err := m.begin() if err != nil { return "", err } if !user.ValidPassword(plaintext) { rollback(tx) return "", user.ErrorInvalidPassword } usr, err := m.insertNewUser(tx, email, false) if err != nil { rollback(tx) return "", err } rid := user.RemoteIdentity{ ConnectorID: connID, ID: usr.ID, } if err := m.addRemoteIdentity(tx, usr.ID, rid); err != nil { rollback(tx) return "", err } password, err := user.NewPasswordFromPlaintext(plaintext) if err != nil { rollback(tx) return "", err } pwi := user.PasswordInfo{ UserID: usr.ID, Password: password, } err = m.pwRepo.Create(tx, pwi) if err != nil { rollback(tx) return "", err } err = tx.Commit() if err != nil { rollback(tx) return "", err } return usr.ID, nil }
func (g *grpcServer) ChangeUserPass(userID string, plaintext string, oldPassword string) error { tx, err := g.begin() if err != nil { return err } if !user.ValidPassword(plaintext) { rollback(tx) return user.ErrorInvalidPassword } pwi, err := g.server.PasswordInfoRepo.Get(tx, userID) if err != nil { rollback(tx) return err } _, err = pwi.Authenticate(oldPassword) if err != nil { rollback(tx) return user.ErrorPasswordHashNoMatch } newPass, err := user.NewPasswordFromPlaintext(plaintext) if err != nil { rollback(tx) return err } pwi.Password = newPass err = g.server.PasswordInfoRepo.Update(tx, pwi) if err != nil { rollback(tx) return err } err = tx.Commit() if err != nil { rollback(tx) return err } return nil }
func (m *UserManager) ChangePassword(pwr PasswordChangeable, plaintext string) (*url.URL, error) { tx, err := m.begin() if err != nil { return nil, err } if !user.ValidPassword(plaintext) { rollback(tx) return nil, user.ErrorInvalidPassword } pwi, err := m.pwRepo.Get(tx, pwr.UserID()) if err != nil { rollback(tx) return nil, err } if string(pwi.Password) != string(pwr.Password()) { rollback(tx) return nil, ErrorPasswordAlreadyChanged } newPass, err := user.NewPasswordFromPlaintext(plaintext) if err != nil { rollback(tx) return nil, err } pwi.Password = newPass err = m.pwRepo.Update(tx, pwi) if err != nil { rollback(tx) return nil, err } err = tx.Commit() if err != nil { rollback(tx) return nil, err } return pwr.Callback(), nil }
func TestCreateAdmin(t *testing.T) { hashedPassword, _ := user.NewPasswordFromPlaintext("foopass") tests := []struct { admn adminschema.Admin wantErr error }{ { //hashed password admn: adminschema.Admin{ Email: "*****@*****.**", Password: string(hashedPassword), }, }, { //plaintext password admn: adminschema.Admin{ Email: "*****@*****.**", Password: "******", }, }, { // duplicate Email admn: adminschema.Admin{ Email: "*****@*****.**", Password: "******", }, wantErr: user.ErrorDuplicateEmail, }, { // bad email admn: adminschema.Admin{ Email: "badEmailexample", Password: "******", }, wantErr: user.ErrorInvalidEmail, }, { // missing Email admn: adminschema.Admin{ Password: "******", }, wantErr: user.ErrorInvalidEmail, }, } for i, tt := range tests { f := makeTestFixtures() id, err := f.adAPI.CreateAdmin(tt.admn) if tt.wantErr != nil { if err == nil { t.Errorf("case %d: err was nil", i) continue } aErr, ok := err.(Error) if !ok { t.Errorf("case %d: not a admin.Error: %#v", i, err) continue } if aErr.Internal != tt.wantErr { t.Errorf("case %d: want=%q, got=%q", i, tt.wantErr, aErr.Internal) continue } } else { if err != nil { t.Errorf("case %d: err != nil: %q", i, err) } gotAdmn, err := f.adAPI.GetAdmin(id) if err != nil { t.Errorf("case %d: err != nil: %q", i, err) } tt.admn.Id = id if diff := pretty.Compare(tt.admn, gotAdmn); diff != "" { t.Errorf("case %d: Compare(want, got) = %v", i, diff) } } } }
func TestHTTPExchangeTokenRefreshToken(t *testing.T) { password, err := user.NewPasswordFromPlaintext("woof") if err != nil { t.Fatalf("unexpectd error: %q", err) } passwordInfo := user.PasswordInfo{ UserID: "elroy77", Password: password, } cfg := &connector.LocalConnectorConfig{ ID: "local", } validRedirURL := url.URL{ Scheme: "http", Host: "client.example.com", Path: "/callback", } ci := client.Client{ Credentials: oidc.ClientCredentials{ ID: validRedirURL.Host, Secret: base64.URLEncoding.EncodeToString([]byte("secret")), }, Metadata: oidc.ClientMetadata{ RedirectURIs: []url.URL{ validRedirURL, }, }, } dbMap := db.NewMemDB() clientRepo, clientManager, err := makeClientRepoAndManager(dbMap, []client.LoadableClient{{ Client: ci, }}) if err != nil { t.Fatalf("Failed to create client identity manager: " + err.Error()) } passwordInfoRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(db.NewMemDB(), []user.PasswordInfo{passwordInfo}) if err != nil { t.Fatalf("Failed to create password info repo: %v", err) } issuerURL := url.URL{Scheme: "http", Host: "server.example.com"} sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap)) k, err := key.GeneratePrivateKey() if err != nil { t.Fatalf("Unable to generate RSA key: %v", err) } km := key.NewPrivateKeyManager() err = km.Set(key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(time.Minute))) if err != nil { t.Fatalf("Unexpected error: %v", err) } usr := user.User{ ID: "ID-test", Email: "*****@*****.**", DisplayName: "displayname", } userRepo := db.NewUserRepo(db.NewMemDB()) if err := userRepo.Create(nil, usr); err != nil { t.Fatalf("Unexpected error: %v", err) } refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo() srv := &server.Server{ IssuerURL: issuerURL, KeyManager: km, SessionManager: sm, ClientRepo: clientRepo, ClientManager: clientManager, Templates: template.New(connector.LoginPageTemplateName), Connectors: []connector.Connector{}, UserRepo: userRepo, PasswordInfoRepo: passwordInfoRepo, RefreshTokenRepo: refreshTokenRepo, } if err = srv.AddConnector(cfg); err != nil { t.Fatalf("Unexpected error: %v", err) } sClient := &phttp.HandlerClient{Handler: srv.HTTPHandler()} pcfg, err := oidc.FetchProviderConfig(sClient, issuerURL.String()) if err != nil { t.Fatalf("Failed to fetch provider config: %v", err) } ks := key.NewPublicKeySet([]jose.JWK{k.JWK()}, time.Now().Add(1*time.Hour)) ccfg := oidc.ClientConfig{ HTTPClient: sClient, ProviderConfig: pcfg, Credentials: ci.Credentials, RedirectURL: validRedirURL.String(), KeySet: *ks, } cl, err := oidc.NewClient(ccfg) if err != nil { t.Fatalf("Failed creating oidc.Client: %v", err) } m := http.NewServeMux() var claims jose.Claims var refresh string m.HandleFunc("/callback", handleCallbackFunc(cl, &claims, &refresh)) cClient := &phttp.HandlerClient{Handler: m} // this will actually happen due to some interaction between the // end-user and a remote identity provider sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access", "email", "profile"}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if _, err = sm.AttachRemoteIdentity(sessionID, passwordInfo.Identity()); err != nil { t.Fatalf("Unexpected error: %v", err) } if _, err = sm.AttachUser(sessionID, usr.ID); err != nil { t.Fatalf("Unexpected error: %v", err) } key, err := sm.NewSessionKey(sessionID) if err != nil { t.Fatalf("Unexpected error: %v", err) } req, err := http.NewRequest("GET", fmt.Sprintf("http://client.example.com/callback?code=%s", key), nil) if err != nil { t.Fatalf("Failed creating HTTP request: %v", err) } resp, err := cClient.Do(req) if err != nil { t.Fatalf("Failed resolving HTTP requests against /callback: %v", err) } if err := verifyUserClaims(claims, &ci, &usr, issuerURL); err != nil { t.Fatalf("Failed to verify claims: %v", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("Received status code %d, want %d", resp.StatusCode, http.StatusOK) } if refresh == "" { t.Fatalf("No refresh token") } // Use refresh token to get a new ID token. token, err := cl.RefreshToken(refresh) if err != nil { t.Fatalf("Unexpected error: %v", err) } claims, err = token.Claims() if err != nil { t.Fatalf("Failed parsing claims from client token: %v", err) } if err := verifyUserClaims(claims, &ci, &usr, issuerURL); err != nil { t.Fatalf("Failed to verify claims: %v", err) } }