3434import io .swagger .v3 .oas .annotations .security .SecurityRequirement ;
3535import io .swagger .v3 .oas .annotations .security .SecurityRequirements ;
3636import io .swagger .v3 .oas .annotations .tags .Tag ;
37+ import org .dependencytrack .auth .Permissions ;
38+ import org .dependencytrack .model .Project ;
39+ import org .dependencytrack .model .validation .ValidUuid ;
40+ import org .dependencytrack .persistence .QueryManager ;
41+ import org .dependencytrack .resources .v1 .openapi .PaginatedApi ;
42+ import org .dependencytrack .resources .v1 .problems .ProblemDetails ;
43+ import org .dependencytrack .resources .v1 .vo .AclMappingRequest ;
44+
3745import jakarta .validation .Validator ;
46+ import jakarta .ws .rs .ClientErrorException ;
3847import jakarta .ws .rs .DELETE ;
3948import jakarta .ws .rs .GET ;
4049import jakarta .ws .rs .PUT ;
4453import jakarta .ws .rs .QueryParam ;
4554import jakarta .ws .rs .core .MediaType ;
4655import jakarta .ws .rs .core .Response ;
47- import org .dependencytrack .auth .Permissions ;
48- import org .dependencytrack .model .Project ;
49- import org .dependencytrack .model .validation .ValidUuid ;
50- import org .dependencytrack .persistence .QueryManager ;
51- import org .dependencytrack .resources .v1 .openapi .PaginatedApi ;
52- import org .dependencytrack .resources .v1 .vo .AclMappingRequest ;
53-
54- import java .util .ArrayList ;
55- import java .util .List ;
56+ import java .util .NoSuchElementException ;
5657
5758/**
5859 * JAX-RS resources for processing LDAP group mapping requests.
@@ -114,10 +115,19 @@ public Response retrieveProjects(@Parameter(description = "The UUID of the team
114115 description = "<p>Requires permission <strong>ACCESS_MANAGEMENT</strong></p>"
115116 )
116117 @ ApiResponses (value = {
117- @ ApiResponse (responseCode = "200" , description = "Mapping created successfully" , content = @ Content (schema = @ Schema (implementation = AclMappingRequest .class ))),
118+ @ ApiResponse (
119+ responseCode = "200" ,
120+ description = "Mapping created successfully" ,
121+ content = @ Content (schema = @ Schema (implementation = AclMappingRequest .class ))),
118122 @ ApiResponse (responseCode = "401" , description = "Unauthorized" ),
119- @ ApiResponse (responseCode = "404" , description = "The UUID of the team or project could not be found" ),
120- @ ApiResponse (responseCode = "409" , description = "A mapping with the same team and project already exists" )
123+ @ ApiResponse (
124+ responseCode = "404" ,
125+ description = "Team or project could not be found" ,
126+ content = @ Content (schema = @ Schema (implementation = ProblemDetails .class ), mediaType = ProblemDetails .MEDIA_TYPE_JSON )),
127+ @ ApiResponse (
128+ responseCode = "409" ,
129+ description = "A mapping with the same team and project already exists" ,
130+ content = @ Content (schema = @ Schema (implementation = ProblemDetails .class ), mediaType = ProblemDetails .MEDIA_TYPE_JSON ))
121131 })
122132 @ PermissionRequired ({Permissions .Constants .ACCESS_MANAGEMENT , Permissions .Constants .ACCESS_MANAGEMENT_CREATE })
123133 public Response addMapping (AclMappingRequest request ) {
@@ -126,21 +136,31 @@ public Response addMapping(AclMappingRequest request) {
126136 validator .validateProperty (request , "team" ),
127137 validator .validateProperty (request , "project" )
128138 );
129- try (QueryManager qm = new QueryManager ()) {
130- final Team team = qm .getObjectByUuid (Team .class , request .getTeam ());
131- final Project project = qm .getObjectByUuid (Project .class , request .getProject ());
132- if (team != null && project != null ) {
133- for (final Team t : project .getAccessTeams ()) {
134- if (t .getUuid () == team .getUuid ()) {
135- return Response .status (Response .Status .CONFLICT ).entity ("A mapping with the same team and project already exists." ).build ();
136- }
139+ try (final var qm = new QueryManager ()) {
140+ qm .runInTransaction (() -> {
141+ final Team team = qm .getObjectByUuid (Team .class , request .getTeam ());
142+ if (team == null ) {
143+ throw new NoSuchElementException ("Team could not be found" );
137144 }
138- project .addAccessTeam (team );
139- qm .persist (project );
140- return Response .ok ().build ();
141- } else {
142- return Response .status (Response .Status .NOT_FOUND ).entity ("The UUID of the team could not be found." ).build ();
143- }
145+
146+ final Project project = qm .getObjectByUuid (Project .class , request .getProject ());
147+ if (project == null ) {
148+ throw new NoSuchElementException ("Project could not be found" );
149+ }
150+
151+ // TODO: The conflict error is legacy behavior, but wouldn't it make more
152+ // sense to return a 304 - Not Modified instead?
153+ final boolean added = project .addAccessTeam (team );
154+ if (!added ) {
155+ final var problemDetails = new ProblemDetails ();
156+ problemDetails .setStatus (409 );
157+ problemDetails .setTitle ("Conflict" );
158+ problemDetails .setDetail ("A mapping with the same team and project already exists" );
159+ throw new ClientErrorException (problemDetails .toResponse ());
160+ }
161+ });
162+
163+ return Response .ok ().build ();
144164 }
145165 }
146166
@@ -154,30 +174,34 @@ public Response addMapping(AclMappingRequest request) {
154174 @ ApiResponses (value = {
155175 @ ApiResponse (responseCode = "200" , description = "Mapping removed successfully" ),
156176 @ ApiResponse (responseCode = "401" , description = "Unauthorized" ),
157- @ ApiResponse (responseCode = "404" , description = "The UUID of the team or project could not be found" ),
177+ @ ApiResponse (
178+ responseCode = "404" ,
179+ description = "Team or project could not be found" ,
180+ content = @ Content (schema = @ Schema (implementation = ProblemDetails .class ), mediaType = ProblemDetails .MEDIA_TYPE_JSON ))
158181 })
159182 @ PermissionRequired ({Permissions .Constants .ACCESS_MANAGEMENT , Permissions .Constants .ACCESS_MANAGEMENT_DELETE })
160183 public Response deleteMapping (
161184 @ Parameter (description = "The UUID of the team to delete the mapping for" , schema = @ Schema (type = "string" , format = "uuid" ), required = true )
162185 @ PathParam ("teamUuid" ) @ ValidUuid String teamUuid ,
163186 @ Parameter (description = "The UUID of the project to delete the mapping for" , schema = @ Schema (type = "string" , format = "uuid" ), required = true )
164187 @ PathParam ("projectUuid" ) @ ValidUuid String projectUuid ) {
165- try (QueryManager qm = new QueryManager ()) {
166- final Team team = qm .getObjectByUuid (Team .class , teamUuid );
167- final Project project = qm .getObjectByUuid (Project .class , projectUuid );
168- if (team != null && project != null ) {
169- final List <Team > teams = new ArrayList <>();
170- for (final Team t : project .getAccessTeams ()) {
171- if (t .getUuid () != team .getUuid ()) {
172- teams .add (t );
173- }
188+ try (final var qm = new QueryManager ()) {
189+ qm .runInTransaction (() -> {
190+ final Team team = qm .getObjectByUuid (Team .class , teamUuid );
191+ if (team == null ) {
192+ throw new NoSuchElementException ("Team could not be found" );
174193 }
175- project .setAccessTeams (teams );
176- qm .persist (project );
177- return Response .ok ().build ();
178- } else {
179- return Response .status (Response .Status .NOT_FOUND ).entity ("The UUID of the team or project could not be found." ).build ();
180- }
194+
195+ final Project project = qm .getObjectByUuid (Project .class , projectUuid );
196+ if (project == null ) {
197+ throw new NoSuchElementException ("Project could not be found" );
198+ }
199+
200+ project .removeAccessTeam (team );
201+ });
181202 }
203+
204+ return Response .ok ().build ();
182205 }
206+
183207}
0 commit comments