Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Group Memberships #323

Merged
merged 11 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/api/httpsrv/handler_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func (h *apiHandler) ListGroups(ctx context.Context, req ListGroupsRequestObject
return nil, permissionsError(err)
}

groups, err := h.engine.ListGroups(ctx, ownerID, req.Params)
groups, err := h.engine.ListGroupsByOwner(ctx, ownerID, req.Params)
if err != nil {
return nil, err
}
Expand Down
234 changes: 234 additions & 0 deletions internal/api/httpsrv/handler_group_members.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
package httpsrv
Dismissed Show dismissed Hide dismissed

import (
"context"
"errors"
"fmt"
"net/http"

"github.com/labstack/echo/v4"
"go.infratographer.com/identity-api/internal/types"
v1 "go.infratographer.com/identity-api/pkg/api/v1"
"go.infratographer.com/permissions-api/pkg/permissions"
"go.infratographer.com/x/gidx"
)

const (
actionGroupMembersList = "iam_group_members_list"
actionGroupMembersAdd = "iam_group_members_add"
actionGroupMembersPut = "iam_group_members_put"
actionGroupMembersRemove = "iam_group_members_remove"
)

// AddGroupMembers creates a group
func (h *apiHandler) AddGroupMembers(ctx context.Context, req AddGroupMembersRequestObject) (AddGroupMembersResponseObject, error) {
reqbody := req.Body
gid := req.GroupID

if _, err := gidx.Parse(string(gid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid owner id: %s", err.Error()),
)

return nil, err
}

for _, mid := range reqbody.MemberIDs {
if _, err := gidx.Parse(string(mid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid member id %s: %s", mid, err.Error()),
)

return nil, err
}
}

if err := permissions.CheckAccess(ctx, gid, actionGroupMembersAdd); err != nil {
return nil, permissionsError(err)
}

if err := h.engine.AddMembers(ctx, gid, reqbody.MemberIDs...); err != nil {
if errors.Is(err, types.ErrNotFound) {
err = echo.NewHTTPError(http.StatusNotFound, err.Error())
}

return nil, err
}

return AddGroupMembers200JSONResponse{Success: true}, nil
}

// ListGroupMembers lists the members of a group
func (h *apiHandler) ListGroupMembers(ctx context.Context, req ListGroupMembersRequestObject) (ListGroupMembersResponseObject, error) {
gid := req.GroupID

if _, err := gidx.Parse(string(gid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid group id: %s", err.Error()),
)

return nil, err
}

if err := permissions.CheckAccess(ctx, gid, actionGroupMembersList); err != nil {
return nil, permissionsError(err)
}

members, err := h.engine.ListMembers(ctx, gid, req.Params)
if err != nil {
if errors.Is(err, types.ErrNotFound) {
err = echo.NewHTTPError(http.StatusNotFound, err.Error())
}

return nil, err
}

collection := v1.GroupMemberCollection{
MemberIDs: members,
GroupID: gid,
Pagination: v1.Pagination{},
}

if err := req.Params.SetPagination(&collection); err != nil {
return nil, err
}

return ListGroupMembers200JSONResponse{GroupMemberCollectionJSONResponse(collection)}, nil
}

// RemoveGroupMember removes a member from a group
func (h *apiHandler) RemoveGroupMember(ctx context.Context, req RemoveGroupMemberRequestObject) (RemoveGroupMemberResponseObject, error) {
gid := req.GroupID
sid := req.SubjectID

if _, err := gidx.Parse(string(gid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid group id: %s", err.Error()),
)

return nil, err
}

if _, err := gidx.Parse(string(sid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid member id: %s", err.Error()),
)
}

if _, err := gidx.Parse(string(sid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid member id: %s", err.Error()),
)

return nil, err
}

if err := permissions.CheckAccess(ctx, gid, actionGroupMembersRemove); err != nil {
return nil, permissionsError(err)
}

if err := h.engine.RemoveMember(ctx, gid, sid); err != nil {
if errors.Is(err, types.ErrNotFound) {
err = echo.NewHTTPError(http.StatusNotFound, err.Error())
}

return nil, err
}

return RemoveGroupMember200JSONResponse{true}, nil
}

// ReplaceGroupMembers replaces the members of a group
func (h *apiHandler) ReplaceGroupMembers(ctx context.Context, req ReplaceGroupMembersRequestObject) (ReplaceGroupMembersResponseObject, error) {
gid := req.GroupID
reqbody := req.Body

if _, err := gidx.Parse(string(gid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid group id: %s", err.Error()),
)

return nil, err
}

for _, mid := range reqbody.MemberIDs {
if _, err := gidx.Parse(string(mid)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid member id %s: %s", mid, err.Error()),
)

return nil, err
}
}

if err := permissions.CheckAccess(ctx, gid, actionGroupMembersPut); err != nil {
return nil, permissionsError(err)
}

if err := h.engine.ReplaceMembers(ctx, gid, reqbody.MemberIDs...); err != nil {
if errors.Is(err, types.ErrNotFound) {
err = echo.NewHTTPError(http.StatusNotFound, err.Error())
}

return nil, err
}

return ReplaceGroupMembers200JSONResponse{true}, nil
}

func (h *apiHandler) ListUserGroups(ctx context.Context, req ListUserGroupsRequestObject) (ListUserGroupsResponseObject, error) {
subject := req.UserID

if _, err := gidx.Parse(string(subject)); err != nil {
err = echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("invalid subject id: %s", err.Error()),
)

return nil, err
}

// Find the owner the user's issuer is on to check permissions.
ownerID, err := h.engine.LookupUserOwnerID(ctx, subject)
switch err {
case nil:
case types.ErrUserInfoNotFound:
return nil, echo.NewHTTPError(http.StatusNotFound, err.Error())
default:
return nil, err
}

if err := permissions.CheckAccess(ctx, ownerID, actionUserGet); err != nil {
return nil, permissionsError(err)
}

groups, err := h.engine.ListGroupsBySubject(ctx, subject, req.Params)
if err != nil {
if errors.Is(err, types.ErrNotFound) {
err = echo.NewHTTPError(http.StatusNotFound, err.Error())
}

return nil, err
}

resp := groups.ToPrefixedIDs()

collection := v1.GroupIDCollection{
GroupIDs: resp,
Pagination: v1.Pagination{},
}

if err := req.Params.SetPagination(&collection); err != nil {
return nil, err
}

return ListUserGroups200JSONResponse{GroupIDCollectionJSONResponse(collection)}, nil
}
Loading
Loading