Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
0
// 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
}
Ejemplo n.º 3
0
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
}
Ejemplo n.º 4
0
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
}
Ejemplo n.º 5
0
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)
			}
		}
	}
}
Ejemplo n.º 6
0
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)
	}
}