ListUsers lists all users.
(ctx context.Context, request *connect.Request[v1pb.ListUsersRequest])
| 132 | |
| 133 | // ListUsers lists all users. |
| 134 | func (s *UserService) ListUsers(ctx context.Context, request *connect.Request[v1pb.ListUsersRequest]) (*connect.Response[v1pb.ListUsersResponse], error) { |
| 135 | offset, err := parseLimitAndOffset(&pageSize{ |
| 136 | token: request.Msg.PageToken, |
| 137 | limit: int(request.Msg.PageSize), |
| 138 | maximum: 1000, |
| 139 | }) |
| 140 | if err != nil { |
| 141 | return nil, err |
| 142 | } |
| 143 | limitPlusOne := offset.limit + 1 |
| 144 | |
| 145 | find := &store.FindUserMessage{ |
| 146 | Limit: &limitPlusOne, |
| 147 | Offset: &offset.offset, |
| 148 | ShowDeleted: request.Msg.ShowDeleted, |
| 149 | } |
| 150 | filterResult, err := store.GetAccountListFilter(request.Msg.Filter) |
| 151 | if err != nil { |
| 152 | return nil, connect.NewError(connect.CodeInvalidArgument, err) |
| 153 | } |
| 154 | find.FilterQ = filterResult.Query |
| 155 | find.ProjectID = filterResult.ProjectID |
| 156 | |
| 157 | // Set workspace scope: |
| 158 | // - When ProjectID is set, workspace is required for the project member CTE query. |
| 159 | // - In SaaS mode (no ProjectID), scope to workspace IAM members to prevent cross-workspace listing. |
| 160 | if find.ProjectID != nil { |
| 161 | find.Workspace = common.GetWorkspaceIDFromContext(ctx) |
| 162 | |
| 163 | user, ok := GetUserFromContext(ctx) |
| 164 | if !ok { |
| 165 | return nil, connect.NewError(connect.CodeInternal, errors.Errorf("user not found")) |
| 166 | } |
| 167 | hasPermission, err := s.iamManager.CheckPermission(ctx, permission.ProjectsGet, user, common.GetWorkspaceIDFromContext(ctx), *find.ProjectID) |
| 168 | if err != nil { |
| 169 | return nil, connect.NewError(connect.CodeInternal, errors.Errorf("failed to check user permission")) |
| 170 | } |
| 171 | if !hasPermission { |
| 172 | return nil, connect.NewError(connect.CodePermissionDenied, errors.Errorf("user does not have permission %q", permission.ProjectsGet)) |
| 173 | } |
| 174 | } else if s.profile.SaaS { |
| 175 | find.Workspace = common.GetWorkspaceIDFromContext(ctx) |
| 176 | } |
| 177 | |
| 178 | users, err := s.store.ListUsers(ctx, find) |
| 179 | if err != nil { |
| 180 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to list user")) |
| 181 | } |
| 182 | |
| 183 | nextPageToken := "" |
| 184 | if len(users) == limitPlusOne { |
| 185 | users = users[:offset.limit] |
| 186 | if nextPageToken, err = offset.getNextPageToken(); err != nil { |
| 187 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to marshal next page token")) |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | response := &v1pb.ListUsersResponse{ |
nothing calls this directly
no test coverage detected