(ctx context.Context, req *adminv1.AddProjectMemberUserRequest)
| 1133 | } |
| 1134 | |
| 1135 | func (s *Server) AddProjectMemberUser(ctx context.Context, req *adminv1.AddProjectMemberUserRequest) (*adminv1.AddProjectMemberUserResponse, error) { |
| 1136 | observability.AddRequestAttributes(ctx, |
| 1137 | attribute.String("args.org", req.Org), |
| 1138 | attribute.String("args.email", req.Email), |
| 1139 | attribute.String("args.project", req.Project), |
| 1140 | attribute.String("args.role", req.Role), |
| 1141 | ) |
| 1142 | if req.RestrictResources != nil { |
| 1143 | observability.AddRequestAttributes(ctx, attribute.Bool("args.restrict_resources", *req.RestrictResources)) |
| 1144 | } |
| 1145 | if len(req.Resources) > 0 { |
| 1146 | observability.AddRequestAttributes(ctx, attribute.StringSlice("args.resources", resourcesString(req.Resources))) |
| 1147 | } |
| 1148 | |
| 1149 | proj, err := s.admin.DB.FindProjectByName(ctx, req.Org, req.Project) |
| 1150 | if err != nil { |
| 1151 | return nil, err |
| 1152 | } |
| 1153 | |
| 1154 | claims := auth.GetClaims(ctx) |
| 1155 | if !claims.ProjectPermissions(ctx, proj.OrganizationID, proj.ID).ManageProjectMembers { |
| 1156 | return nil, status.Error(codes.PermissionDenied, "not allowed to add project members") |
| 1157 | } |
| 1158 | |
| 1159 | // Check outstanding invites quota |
| 1160 | count, err := s.admin.DB.CountInvitesForOrganization(ctx, proj.OrganizationID) |
| 1161 | if err != nil { |
| 1162 | return nil, err |
| 1163 | } |
| 1164 | org, err := s.admin.DB.FindOrganization(ctx, proj.OrganizationID) |
| 1165 | if err != nil { |
| 1166 | return nil, err |
| 1167 | } |
| 1168 | if org.QuotaOutstandingInvites >= 0 && count >= org.QuotaOutstandingInvites { |
| 1169 | return nil, status.Errorf(codes.FailedPrecondition, "quota exceeded: org %q can at most have %d outstanding invitations", org.Name, org.QuotaOutstandingInvites) |
| 1170 | } |
| 1171 | |
| 1172 | role, err := s.admin.DB.FindProjectRole(ctx, req.Role) |
| 1173 | if err != nil { |
| 1174 | return nil, err |
| 1175 | } |
| 1176 | if role.Admin && !claims.ProjectPermissions(ctx, proj.OrganizationID, proj.ID).ManageProjectAdmins { |
| 1177 | return nil, status.Error(codes.PermissionDenied, "as a non-admin you are not allowed to assign an admin role") |
| 1178 | } |
| 1179 | |
| 1180 | var invitedByUserID, invitedByName string |
| 1181 | if claims.OwnerType() == auth.OwnerTypeUser { |
| 1182 | user, err := s.admin.DB.FindUser(ctx, claims.OwnerID()) |
| 1183 | if err != nil && !errors.Is(err, database.ErrNotFound) { |
| 1184 | return nil, err |
| 1185 | } |
| 1186 | if user != nil { |
| 1187 | invitedByUserID = user.ID |
| 1188 | invitedByName = user.DisplayName |
| 1189 | } |
| 1190 | } |
| 1191 | |
| 1192 | keepExistingRestrictions := req.RestrictResources == nil && len(req.Resources) == 0 |
nothing calls this directly
no test coverage detected