| .. | .. |
|---|
| 1 | +/* |
|---|
| 2 | + * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved. |
|---|
| 3 | + */ |
|---|
| 1 | 4 | package net.curisit.securis.services; |
|---|
| 2 | 5 | |
|---|
| 3 | 6 | import java.util.Date; |
|---|
| .. | .. |
|---|
| 39 | 42 | import net.curisit.securis.utils.TokenHelper; |
|---|
| 40 | 43 | |
|---|
| 41 | 44 | /** |
|---|
| 42 | | - * Organization resource, this service will provide methods to create, modify |
|---|
| 43 | | - * and delete organizations |
|---|
| 44 | | - * |
|---|
| 45 | | - * @author roberto <roberto.sanchez@curisit.net> |
|---|
| 45 | + * OrganizationResource |
|---|
| 46 | + * <p> |
|---|
| 47 | + * CRUD and listing of organizations. Non-admin users are scoped by their |
|---|
| 48 | + * accessible organization ids when listing. |
|---|
| 49 | + * |
|---|
| 50 | + * Last reviewed by JRA on Oct 5, 2025. |
|---|
| 46 | 51 | */ |
|---|
| 47 | 52 | @Path("/organization") |
|---|
| 48 | 53 | @RequestScoped |
|---|
| .. | .. |
|---|
| 50 | 55 | |
|---|
| 51 | 56 | private static final Logger LOG = LogManager.getLogger(OrganizationResource.class); |
|---|
| 52 | 57 | |
|---|
| 53 | | - @Context |
|---|
| 54 | | - EntityManager em; |
|---|
| 58 | + @Context EntityManager em; |
|---|
| 59 | + @Context BasicSecurityContext bsc; |
|---|
| 55 | 60 | |
|---|
| 56 | | - @Context |
|---|
| 57 | | - BasicSecurityContext bsc; |
|---|
| 58 | | - |
|---|
| 59 | | - public OrganizationResource() { |
|---|
| 60 | | - } |
|---|
| 61 | + public OrganizationResource() { } |
|---|
| 61 | 62 | |
|---|
| 62 | 63 | /** |
|---|
| 63 | | - * |
|---|
| 64 | | - * @return the server version in format majorVersion.minorVersion |
|---|
| 64 | + * index |
|---|
| 65 | + * <p> |
|---|
| 66 | + * List organizations. For admins returns all; for non-admins filters |
|---|
| 67 | + * by the ids in {@link BasicSecurityContext#getOrganizationsIds()}. |
|---|
| 68 | + * |
|---|
| 69 | + * @return 200 OK with the list. |
|---|
| 65 | 70 | */ |
|---|
| 66 | 71 | @GET |
|---|
| 67 | 72 | @Path("/") |
|---|
| .. | .. |
|---|
| 69 | 74 | @Securable |
|---|
| 70 | 75 | public Response index() { |
|---|
| 71 | 76 | LOG.info("Getting organizations list "); |
|---|
| 72 | | - |
|---|
| 73 | | - // EntityManager em = emProvider.get(); |
|---|
| 74 | 77 | em.clear(); |
|---|
| 75 | 78 | TypedQuery<Organization> q; |
|---|
| 76 | 79 | if (bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) { |
|---|
| .. | .. |
|---|
| 84 | 87 | q.setParameter("list_ids", bsc.getOrganizationsIds()); |
|---|
| 85 | 88 | } |
|---|
| 86 | 89 | } |
|---|
| 87 | | - |
|---|
| 88 | 90 | List<Organization> list = q.getResultList(); |
|---|
| 89 | | - |
|---|
| 90 | 91 | return Response.ok(list).build(); |
|---|
| 91 | 92 | } |
|---|
| 92 | 93 | |
|---|
| 93 | 94 | /** |
|---|
| 94 | | - * |
|---|
| 95 | | - * @return the server version in format majorVersion.minorVersion |
|---|
| 95 | + * get |
|---|
| 96 | + * <p> |
|---|
| 97 | + * Fetch an organization by id. |
|---|
| 98 | + * |
|---|
| 99 | + * @param orgid organization id (string form). |
|---|
| 100 | + * @param token header token (unused). |
|---|
| 101 | + * @return 200 OK with entity or 404 if not found. |
|---|
| 96 | 102 | */ |
|---|
| 97 | 103 | @GET |
|---|
| 98 | 104 | @Path("/{orgid}") |
|---|
| .. | .. |
|---|
| 104 | 110 | LOG.error("Organization ID is mandatory"); |
|---|
| 105 | 111 | return Response.status(Status.NOT_FOUND).build(); |
|---|
| 106 | 112 | } |
|---|
| 107 | | - |
|---|
| 108 | | - // EntityManager em = emProvider.get(); |
|---|
| 109 | 113 | em.clear(); |
|---|
| 110 | 114 | Organization org = em.find(Organization.class, Integer.parseInt(orgid)); |
|---|
| 111 | 115 | if (org == null) { |
|---|
| .. | .. |
|---|
| 115 | 119 | return Response.ok(org).build(); |
|---|
| 116 | 120 | } |
|---|
| 117 | 121 | |
|---|
| 118 | | - private boolean isCyclicalRelationship(int currentId, Organization parent) { |
|---|
| 119 | | - while (parent != null) { |
|---|
| 120 | | - if (parent.getId() == currentId) { |
|---|
| 121 | | - return true; |
|---|
| 122 | | - } |
|---|
| 123 | | - parent = parent.getParentOrganization(); |
|---|
| 124 | | - } |
|---|
| 125 | | - return false; |
|---|
| 126 | | - } |
|---|
| 127 | | - |
|---|
| 122 | + /** |
|---|
| 123 | + * create |
|---|
| 124 | + * <p> |
|---|
| 125 | + * Create a new organization, setting optional parent and user members. |
|---|
| 126 | + * Requires ADMIN. |
|---|
| 127 | + * |
|---|
| 128 | + * @param org payload with code/name/etc., optional parentOrgId and usersIds. |
|---|
| 129 | + * @return 200 OK with created organization or 404 when parent/user not found. |
|---|
| 130 | + */ |
|---|
| 128 | 131 | @POST |
|---|
| 129 | 132 | @Path("/") |
|---|
| 130 | 133 | @Consumes(MediaType.APPLICATION_JSON) |
|---|
| .. | .. |
|---|
| 134 | 137 | @RolesAllowed(BasicSecurityContext.ROL_ADMIN) |
|---|
| 135 | 138 | public Response create(Organization org) { |
|---|
| 136 | 139 | LOG.info("Creating new organization"); |
|---|
| 137 | | - // EntityManager em = emProvider.get(); |
|---|
| 138 | 140 | |
|---|
| 139 | 141 | try { |
|---|
| 140 | 142 | this.setParentOrg(org, org.getParentOrgId(), em); |
|---|
| .. | .. |
|---|
| 162 | 164 | return Response.ok(org).build(); |
|---|
| 163 | 165 | } |
|---|
| 164 | 166 | |
|---|
| 165 | | - private void setParentOrg(Organization org, Integer parentOrgId, EntityManager em) throws SeCurisException { |
|---|
| 166 | | - Organization parentOrg = null; |
|---|
| 167 | | - if (parentOrgId != null) { |
|---|
| 168 | | - parentOrg = em.find(Organization.class, parentOrgId); |
|---|
| 169 | | - if (parentOrg == null) { |
|---|
| 170 | | - LOG.error("Organization parent with id {} not found in DB", org.getParentOrgId()); |
|---|
| 171 | | - throw new SecurityException("Organization's parent not found with ID: " + org.getParentOrgId()); |
|---|
| 172 | | - } |
|---|
| 173 | | - } |
|---|
| 174 | | - |
|---|
| 175 | | - org.setParentOrganization(parentOrg); |
|---|
| 176 | | - } |
|---|
| 177 | | - |
|---|
| 178 | | - private void setOrgUsers(Organization org, Set<String> usersIds, EntityManager em) throws SeCurisException { |
|---|
| 179 | | - Set<User> users = null; |
|---|
| 180 | | - if (usersIds != null && !usersIds.isEmpty()) { |
|---|
| 181 | | - users = new HashSet<>(); |
|---|
| 182 | | - for (String username : usersIds) { |
|---|
| 183 | | - User user = em.find(User.class, username); |
|---|
| 184 | | - if (user == null) { |
|---|
| 185 | | - LOG.error("Organization user with id '{}' not found in DB", username); |
|---|
| 186 | | - throw new SecurityException("Organization's user not found with ID: " + username); |
|---|
| 187 | | - } |
|---|
| 188 | | - users.add(user); |
|---|
| 189 | | - } |
|---|
| 190 | | - } |
|---|
| 191 | | - |
|---|
| 192 | | - org.setUsers(users); |
|---|
| 193 | | - } |
|---|
| 194 | | - |
|---|
| 167 | + /** |
|---|
| 168 | + * modify |
|---|
| 169 | + * <p> |
|---|
| 170 | + * Update an organization. Validates no cyclic parent relationship, |
|---|
| 171 | + * updates parent and user set. Requires ADMIN. |
|---|
| 172 | + * |
|---|
| 173 | + * @param org new values (including optional parent/usersIds). |
|---|
| 174 | + * @param orgid target id. |
|---|
| 175 | + * @param token (unused) header token. |
|---|
| 176 | + * @return 200 OK with updated organization, or specific error status. |
|---|
| 177 | + */ |
|---|
| 195 | 178 | @PUT |
|---|
| 196 | 179 | @POST |
|---|
| 197 | 180 | @Path("/{orgid}") |
|---|
| .. | .. |
|---|
| 202 | 185 | @RolesAllowed(BasicSecurityContext.ROL_ADMIN) |
|---|
| 203 | 186 | public Response modify(Organization org, @PathParam("orgid") String orgid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) { |
|---|
| 204 | 187 | LOG.info("Modifying organization with id: {}", orgid); |
|---|
| 205 | | - // EntityManager em = emProvider.get(); |
|---|
| 206 | 188 | Organization currentOrg = em.find(Organization.class, Integer.parseInt(orgid)); |
|---|
| 207 | 189 | if (currentOrg == null) { |
|---|
| 208 | 190 | LOG.error("Organization with id {} not found in DB", orgid); |
|---|
| .. | .. |
|---|
| 233 | 215 | return Response.ok(currentOrg).build(); |
|---|
| 234 | 216 | } |
|---|
| 235 | 217 | |
|---|
| 218 | + /** |
|---|
| 219 | + * delete |
|---|
| 220 | + * <p> |
|---|
| 221 | + * Delete an organization if it has no children. Requires ADMIN. |
|---|
| 222 | + * |
|---|
| 223 | + * @param orgid target id. |
|---|
| 224 | + * @param req request (unused). |
|---|
| 225 | + * @return 200 OK with success map, or 404/403 on constraints. |
|---|
| 226 | + */ |
|---|
| 236 | 227 | @DELETE |
|---|
| 237 | 228 | @Path("/{orgid}") |
|---|
| 238 | 229 | @EnsureTransaction |
|---|
| 239 | 230 | @Produces({ MediaType.APPLICATION_JSON }) |
|---|
| 240 | 231 | @Securable(roles = Rol.ADMIN) |
|---|
| 241 | 232 | @RolesAllowed(BasicSecurityContext.ROL_ADMIN) |
|---|
| 242 | | - public Response delete(@PathParam("orgid") String orgid, @Context HttpServletRequest request) { |
|---|
| 233 | + public Response delete(@PathParam("orgid") String orgid, @Context HttpServletRequest req) { |
|---|
| 243 | 234 | LOG.info("Deleting organization with id: {}", orgid); |
|---|
| 244 | | - // EntityManager em = emProvider.get(); |
|---|
| 245 | 235 | Organization org = em.find(Organization.class, Integer.parseInt(orgid)); |
|---|
| 246 | 236 | if (org == null) { |
|---|
| 247 | 237 | LOG.error("Organization with id {} can not be deleted, It was not found in DB", orgid); |
|---|
| .. | .. |
|---|
| 256 | 246 | return Response.ok(Utils.createMap("success", true, "id", orgid)).build(); |
|---|
| 257 | 247 | } |
|---|
| 258 | 248 | |
|---|
| 249 | + // --------------------------------------------------------------------- |
|---|
| 250 | + // Helpers |
|---|
| 251 | + // --------------------------------------------------------------------- |
|---|
| 252 | + |
|---|
| 253 | + /** |
|---|
| 254 | + * isCyclicalRelationship<p> |
|---|
| 255 | + * Detects cycles by walking up the parent chain. |
|---|
| 256 | + * |
|---|
| 257 | + * @param currentId |
|---|
| 258 | + * @param parent |
|---|
| 259 | + * @return isCyclicalRelationship |
|---|
| 260 | + */ |
|---|
| 261 | + private boolean isCyclicalRelationship(int currentId, Organization parent) { |
|---|
| 262 | + while (parent != null) { |
|---|
| 263 | + if (parent.getId() == currentId) return true; |
|---|
| 264 | + parent = parent.getParentOrganization(); |
|---|
| 265 | + } |
|---|
| 266 | + return false; |
|---|
| 267 | + } |
|---|
| 268 | + |
|---|
| 269 | + /** |
|---|
| 270 | + * setParentOrg<p> |
|---|
| 271 | + * Resolve and set parent organization (nullable). |
|---|
| 272 | + * |
|---|
| 273 | + * @param org |
|---|
| 274 | + * @param parentOrgId |
|---|
| 275 | + * @param entitymanager |
|---|
| 276 | + * @throws SeCurisException |
|---|
| 277 | + */ |
|---|
| 278 | + private void setParentOrg(Organization org, Integer parentOrgId, EntityManager em) throws SeCurisException { |
|---|
| 279 | + Organization parentOrg = null; |
|---|
| 280 | + if (parentOrgId != null) { |
|---|
| 281 | + parentOrg = em.find(Organization.class, parentOrgId); |
|---|
| 282 | + if (parentOrg == null) { |
|---|
| 283 | + LOG.error("Organization parent with id {} not found in DB", org.getParentOrgId()); |
|---|
| 284 | + throw new SecurityException("Organization's parent not found with ID: " + org.getParentOrgId()); |
|---|
| 285 | + } |
|---|
| 286 | + } |
|---|
| 287 | + org.setParentOrganization(parentOrg); |
|---|
| 288 | + } |
|---|
| 289 | + |
|---|
| 290 | + /** |
|---|
| 291 | + * setOrgUsers<p> |
|---|
| 292 | + * Replace organization users from the provided usernames set. |
|---|
| 293 | + * |
|---|
| 294 | + * @param org |
|---|
| 295 | + * @param usersIds |
|---|
| 296 | + * @param entityManager |
|---|
| 297 | + * @throws SeCurisException |
|---|
| 298 | + */ |
|---|
| 299 | + private void setOrgUsers(Organization org, Set<String> usersIds, EntityManager em) throws SeCurisException { |
|---|
| 300 | + Set<User> users = null; |
|---|
| 301 | + if (usersIds != null && !usersIds.isEmpty()) { |
|---|
| 302 | + users = new HashSet<>(); |
|---|
| 303 | + for (String username : usersIds) { |
|---|
| 304 | + User user = em.find(User.class, username); |
|---|
| 305 | + if (user == null) { |
|---|
| 306 | + LOG.error("Organization user with id '{}' not found in DB", username); |
|---|
| 307 | + throw new SecurityException("Organization's user not found with ID: " + username); |
|---|
| 308 | + } |
|---|
| 309 | + users.add(user); |
|---|
| 310 | + } |
|---|
| 311 | + } |
|---|
| 312 | + org.setUsers(users); |
|---|
| 313 | + } |
|---|
| 259 | 314 | } |
|---|
| 315 | + |
|---|