package service import ( "context" "sort" "strings" "github.com/keys-pub/keys" "github.com/keys-pub/keys/api" "github.com/keys-pub/keys/user" "github.com/pkg/errors" ) // Keys (RPC) ... func (s *service) Keys(ctx context.Context, req *KeysRequest) (*KeysResponse, error) { query := strings.TrimSpace(req.Query) sortField := req.SortField if sortField == "" { sortField = "user" } sortDirection := req.SortDirection vks, err := s.vault.Keys() if err != nil { return nil, err } out, err := s.filterKeys(ctx, vks, true, query, req.Types, sortField, sortDirection) if err != nil { return nil, err } return &KeysResponse{ Keys: out, SortField: sortField, SortDirection: sortDirection, }, nil } func hasType(k *api.Key, types []string) bool { for _, t := range types { if k.Type == t { return true } } return false } func (s *service) filterKeys(ctx context.Context, ks []*api.Key, saved bool, query string, types []string, sortField string, sortDirection SortDirection) ([]*Key, error) { keys := make([]*Key, 0, len(ks)) for _, k := range ks { if len(types) != 0 && !hasType(k, types) { continue } key, err := s.keyToRPC(ctx, k, saved) if err != nil { return nil, err } if query == "" || (key.User != nil && strings.HasPrefix(key.User.ID, query)) || strings.HasPrefix(key.ID, query) { keys = append(keys, key) } } switch sortField { case "kid", "user", "type": default: return nil, errors.Errorf("invalid sort field") } sort.Slice(keys, func(i, j int) bool { return keysSort(keys, sortField, sortDirection, i, j) }) return keys, nil } func keysSort(pks []*Key, sortField string, sortDirection SortDirection, i, j int) bool { switch sortField { case "type": if pks[i].Type == pks[j].Type { return keysSort(pks, "user", sortDirection, i, j) } if sortDirection == SortDesc { return pks[i].Type < pks[j].Type } return pks[i].Type > pks[j].Type case "user": if pks[i].User == nil && pks[j].User == nil { return keysSort(pks, "kid", sortDirection, i, j) } else if pks[i].User == nil { return false } else if pks[j].User == nil { return true } if sortDirection == SortDesc { return pks[i].User.Name > pks[j].User.Name } return pks[i].User.Name <= pks[j].User.Name default: if sortDirection == SortDesc { return pks[i].ID > pks[j].ID } return pks[i].ID <= pks[j].ID } } // func (s *service) resolveKeys(ctx context.Context, kids []keys.ID) ([]*Key, error) { // out := make([]*Key, 0, len(kids)) // for _, kid := range kids { // key, err := s.resolveKey(ctx, kid) // if err != nil { // return nil, err // } // out = append(out, key) // } // return out, nil // } func (s *service) resolveKey(ctx context.Context, kid keys.ID) (*Key, error) { // TODO: If the user was revoked, this could update every request. // The server should remove the key from channel membership on user revocation? if _, err := s.userResultOrUpdate(ctx, kid); err != nil { return nil, err } key, err := s.key(ctx, kid) if err != nil { return nil, err } return key, nil } // Check if we have user, if not update. func (s *service) userResultOrUpdate(ctx context.Context, kid keys.ID) (*user.Result, error) { user, err := s.users.Get(ctx, kid) if err != nil { return nil, err } if user != nil { return s.users.Get(ctx, kid) } return s.updateUser(ctx, kid, false) }