Ctrl K
ComunidadGuías…

coderd/experiments/dbstore.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package experiments

import (
	"context"
	"database/sql"
	"slices"

	"github.com/google/uuid"
	"golang.org/x/xerrors"

	"github.com/coder/coder/v2/coderd/database"
	"github.com/coder/coder/v2/coderd/database/dbauthz"
	"github.com/coder/coder/v2/codersdk"
)

// NewDBStore returns a Store that reads rules and subject attributes from
// db. Reads run as the system: the Evaluator decides for any subject, not
// for the caller.
func NewDBStore(db database.Store) Store {
	return dbStore{db: db}
}

type dbStore struct {
	db database.Store
}

// Rules implements Store. Rows for unknown experiments are returned keyed
// by their name, and values are returned undecoded; the Evaluator ignores
// the former and decides off for undecodable values.
func (s dbStore) Rules(ctx context.Context) (map[codersdk.Experiment]StoredRule, error) {
	//nolint:gocritic // The Evaluator reads rules for every subject.
	rows, err := s.db.GetExperimentRules(dbauthz.AsSystemRestricted(ctx))
	if err != nil {
		return nil, xerrors.Errorf("get experiment rules: %w", err)
	}
	rules := make(map[codersdk.Experiment]StoredRule, len(rows))
	for _, row := range rows {
		rules[codersdk.Experiment(row.Experiment)] = StoredRule{Value: []byte(row.Value)}
	}
	return rules, nil
}

// UserAttributes implements Store.
func (s dbStore) UserAttributes(ctx context.Context, userID uuid.UUID) (User, error) {
	//nolint:gocritic // Conditions read the subject's attributes, not the caller's.
	ctx = dbauthz.AsSystemRestricted(ctx)
	user, err := s.db.GetUserByID(ctx, userID)
	if err != nil {
		return User{}, xerrors.Errorf("get user: %w", err)
	}
	orgs, err := s.db.GetOrganizationsByUserID(ctx, database.GetOrganizationsByUserIDParams{
		UserID:  userID,
		Deleted: sql.NullBool{Bool: false, Valid: true},
	})
	if err != nil {
		return User{}, xerrors.Errorf("get user organizations: %w", err)
	}
	groups, err := s.db.GetGroups(ctx, database.GetGroupsParams{HasMemberID: userID})
	if err != nil {
		return User{}, xerrors.Errorf("get user groups: %w", err)
	}

	attrs := User{
		ID:            user.ID.String(),
		Username:      user.Username,
		Email:         user.Email,
		Roles:         append([]string{}, user.RBACRoles...),
		Organizations: make([]string, 0, len(orgs)),
		Groups:        make([]string, 0, len(groups)),
	}
	activeOrgs := make(map[uuid.UUID]struct{}, len(orgs))
	for _, org := range orgs {
		activeOrgs[org.ID] = struct{}{}
		attrs.Organizations = append(attrs.Organizations, org.Name)
	}
	for _, group := range groups {
		// GetGroups also returns groups of soft-deleted organizations,
		// whose names can be reused by new organizations.
		if _, ok := activeOrgs[group.Group.OrganizationID]; !ok {
			continue
		}
		attrs.Groups = append(attrs.Groups, group.OrganizationName+"/"+group.Group.Name)
	}
	// Queries return rows in no guaranteed order. Sort so that conditions
	// indexing or comparing whole lists decide the same way every time.
	slices.Sort(attrs.Roles)
	slices.Sort(attrs.Organizations)
	slices.Sort(attrs.Groups)
	return attrs, nil
}