Skip to content

Commit ae1e0d8

Browse files
Merge pull request #67
fix/improve-rest-apis
2 parents 60a47fd + 2237844 commit ae1e0d8

3 files changed

Lines changed: 94 additions & 20 deletions

File tree

extensions/saas/sources/core/src/main/java/tools/dynamia/modules/saas/controllers/AccountApiController.java

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
package tools.dynamia.modules.saas.controllers;
1919

2020
import org.springframework.beans.factory.annotation.Autowired;
21+
import org.springframework.http.HttpStatus;
22+
import org.springframework.http.ResponseEntity;
2123
import org.springframework.util.MimeTypeUtils;
2224
import org.springframework.web.bind.annotation.*;
2325
import tools.dynamia.domain.query.QueryConditions;
2426
import tools.dynamia.domain.query.QueryParameters;
2527
import tools.dynamia.domain.services.AbstractService;
28+
import tools.dynamia.modules.saas.api.AccountServiceAPI;
2629
import tools.dynamia.modules.saas.api.AccountStatsList;
2730
import tools.dynamia.modules.saas.api.dto.AccountDTO;
2831
import tools.dynamia.modules.saas.api.enums.AccountPeriodicity;
@@ -37,6 +40,7 @@
3740

3841
import java.time.LocalDateTime;
3942
import java.util.Date;
43+
import java.util.Map;
4044

4145
@RestController
4246
@RequestMapping(value = "/api/saas", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
@@ -53,15 +57,20 @@ public class AccountApiController extends AbstractService {
5357
}
5458

5559
private final AccountService service;
60+
private final AccountServiceAPI serviceAPI;
5661

5762
@Autowired
58-
public AccountApiController(AccountService service) {
63+
public AccountApiController(AccountService service, AccountServiceAPI serviceAPI) {
5964
this.service = service;
65+
this.serviceAPI = serviceAPI;
6066
}
6167

6268
@GetMapping("/account/{uuid}")
63-
public AccountDTO getAccount(@PathVariable("uuid") String uuid, HttpServletRequest request) {
69+
public ResponseEntity<AccountDTO> getAccount(@PathVariable("uuid") String uuid, HttpServletRequest request) {
6470

71+
if (!isAuthorized(request) && !isSameAccount(uuid, request)) {
72+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
73+
}
6574

6675
Account account = getAccount(uuid);
6776

@@ -75,28 +84,31 @@ public AccountDTO getAccount(@PathVariable("uuid") String uuid, HttpServletReque
7584
accountDTO.setStatusDescription("Licencia invalida");
7685
}
7786
}
78-
return accountDTO;
87+
return ResponseEntity.ok(accountDTO);
7988
}
8089

8190

82-
return NO_ACCOUNT;
91+
return ResponseEntity.ok(NO_ACCOUNT);
8392

8493
}
8594

8695
@PostMapping("/account/{uuid}/stats")
87-
public String updateStats(@PathVariable("uuid") String uuid, @RequestBody AccountStatsList stats, HttpServletRequest request) {
96+
public ResponseEntity<Map<String, String>> updateStats(@PathVariable("uuid") String uuid, @RequestBody AccountStatsList stats, HttpServletRequest request) {
8897

98+
if (!isAuthorized(request) && !isSameAccount(uuid, request)) {
99+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
100+
}
89101

90102
Account account = getAccount(uuid);
91103
if (account != null) {
92104
if (stats != null) {
93105
service.updateStats(account, stats.getData());
94-
return "DONE: Account stats updated";
106+
return ResponseEntity.ok(Map.of("message", "DONE: Account stats updated"));
95107
} else {
96-
return "Invalid stats";
108+
return ResponseEntity.ok(Map.of("message", "Invalid stats"));
97109
}
98110
} else {
99-
return "Cannot found account with uuid: " + uuid;
111+
return ResponseEntity.notFound().build();
100112
}
101113
}
102114

@@ -110,7 +122,12 @@ private Account getAccount(@PathVariable("uuid") String uuid) {
110122
}
111123

112124
@GetMapping("/account/{uuid}/parameter/{name}")
113-
public String getAccountParameter(@PathVariable("uuid") String uuid, @PathVariable("name") String name, HttpServletRequest request) {
125+
public ResponseEntity<Map<String, String>> getAccountParameter(@PathVariable("uuid") String uuid, @PathVariable("name") String name, HttpServletRequest request) {
126+
127+
if (!isAuthorized(request) && !isSameAccount(uuid, request)) {
128+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
129+
}
130+
114131
String defautlValue = request.getParameter("defaultValue");
115132
String value = null;
116133
Account account = getAccount(uuid);
@@ -129,7 +146,11 @@ public String getAccountParameter(@PathVariable("uuid") String uuid, @PathVariab
129146
});
130147
}
131148
}
132-
return value;
149+
if (value != null) {
150+
return ResponseEntity.ok(Map.of("parameter", name, "value", value));
151+
} else {
152+
return ResponseEntity.notFound().build();
153+
}
133154
}
134155

135156
private void newLog(String uuid, HttpServletRequest request, Account account) {
@@ -142,4 +163,18 @@ private void newLog(String uuid, HttpServletRequest request, Account account) {
142163

143164
}
144165
}
166+
167+
public boolean isAuthorized(HttpServletRequest request) {
168+
if (serviceAPI.getSystemAccountId().equals(serviceAPI.getCurrentAccountId())) {
169+
return true;
170+
}
171+
172+
return false;
173+
}
174+
175+
public boolean isSameAccount(String uuid, HttpServletRequest request) {
176+
var subdomain = HttpUtils.getSubdomain(request);
177+
var currentAccount = service.getAccount(subdomain);
178+
return uuid.equals(currentAccount.getUuid());
179+
}
145180
}

platform/app/src/main/java/tools/dynamia/app/controllers/AbstractCrudServiceRestController.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
import io.swagger.v3.oas.annotations.tags.Tag;
55
import org.springframework.http.ResponseEntity;
66
import org.springframework.web.bind.annotation.*;
7+
import tools.dynamia.commons.SimpleCache;
78
import tools.dynamia.commons.StringPojoParser;
89
import tools.dynamia.domain.ValidationError;
910
import tools.dynamia.domain.query.QueryParameters;
1011
import tools.dynamia.domain.services.CrudService;
12+
import tools.dynamia.integration.Containers;
13+
import tools.dynamia.viewers.ViewDescriptorFactory;
1114
import tools.jackson.core.JacksonException;
1215
import tools.jackson.databind.json.JsonMapper;
1316

@@ -46,8 +49,11 @@ public abstract class AbstractCrudServiceRestController {
4649
*/
4750
private final JsonMapper mapper = StringPojoParser.createJsonMapper();
4851

52+
private SimpleCache<String, Class> allowedClasses = new SimpleCache<>();
53+
4954
/**
5055
* Constructs a new {@code CrudServiceRestController} with the given CRUD service.
56+
*
5157
* @param crudService the CRUD service to use
5258
*/
5359
public AbstractCrudServiceRestController(CrudService crudService) {
@@ -56,8 +62,9 @@ public AbstractCrudServiceRestController(CrudService crudService) {
5662

5763
/**
5864
* Creates or updates an entity of the specified class.
65+
*
5966
* @param className the fully qualified class name of the entity
60-
* @param json the JSON representation of the entity
67+
* @param json the JSON representation of the entity
6168
* @return the persisted entity
6269
*/
6370
@RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
@@ -69,8 +76,9 @@ public ResponseEntity<Object> createOrSave(@PathVariable String className, @Requ
6976

7077
/**
7178
* Deletes an entity by its class name and ID.
79+
*
7280
* @param className the fully qualified class name of the entity
73-
* @param id the entity ID
81+
* @param id the entity ID
7482
* @return the deleted entity ID if successful, or 404 if not found
7583
*/
7684
@DeleteMapping("/{id}")
@@ -87,8 +95,9 @@ public ResponseEntity<Object> delete(@PathVariable String className, @PathVariab
8795

8896
/**
8997
* Retrieves an entity by its class name and ID.
98+
*
9099
* @param className the fully qualified class name of the entity
91-
* @param id the entity ID
100+
* @param id the entity ID
92101
* @return the entity if found, or 404 if not found
93102
*/
94103
@GetMapping("/{id}")
@@ -103,7 +112,8 @@ public ResponseEntity<Object> get(@PathVariable String className, @PathVariable
103112

104113
/**
105114
* Finds entities by query parameters.
106-
* @param className the fully qualified class name of the entity
115+
*
116+
* @param className the fully qualified class name of the entity
107117
* @param parameters the query parameters
108118
* @return the list of matching entities
109119
*/
@@ -116,7 +126,8 @@ public ResponseEntity<List<Object>> find(@PathVariable String className, @Reques
116126

117127
/**
118128
* Gets the ID of an entity by query parameters.
119-
* @param className the fully qualified class name of the entity
129+
*
130+
* @param className the fully qualified class name of the entity
120131
* @param parameters the query parameters
121132
* @return the entity ID
122133
*/
@@ -129,8 +140,9 @@ public ResponseEntity<Object> getId(@PathVariable String className, @RequestBody
129140

130141
/**
131142
* Parses a JSON string into an entity object of the specified class.
143+
*
132144
* @param className the fully qualified class name
133-
* @param json the JSON string
145+
* @param json the JSON string
134146
* @return the entity object
135147
* @throws ValidationError if parsing fails
136148
*/
@@ -145,15 +157,29 @@ private Object parseJson(String className, String json) {
145157

146158
/**
147159
* Loads a class by its fully qualified name.
160+
*
148161
* @param className the class name
149162
* @return the {@link Class} object
150163
* @throws ValidationError if the class is not found
151164
*/
152165
private Class loadClass(String className) {
153-
try {
154-
return Class.forName(className);
155-
} catch (ClassNotFoundException e) {
156-
throw new ValidationError("Class not found " + className + " - " + e.getMessage(), e);
166+
initAllowedClasses();
167+
Class entityClass = allowedClasses.get(className);
168+
if (entityClass == null) {
169+
throw new ValidationError("Class not allowed: " + className);
170+
}
171+
return entityClass;
172+
}
173+
174+
private void initAllowedClasses() {
175+
if (allowedClasses == null || allowedClasses.isEmpty()) {
176+
ViewDescriptorFactory viewDescriptorFactory = Containers.get().findObject(ViewDescriptorFactory.class);
177+
if (viewDescriptorFactory != null) {
178+
viewDescriptorFactory.findDescriptorsByType("crud").forEach(d -> {
179+
var entityClass = d.getKey();
180+
allowedClasses.add(entityClass.getName(), entityClass);
181+
});
182+
}
157183
}
158184
}
159185
}

platform/core/commons/src/main/java/tools/dynamia/commons/SimpleCache.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ public void add(K key, V value) {
5353
data.put(key, value);
5454
}
5555

56+
/**
57+
* Alias to add
58+
* @param key
59+
* @param value
60+
*/
61+
public void put(K key, V value) {
62+
data.put(key, value);
63+
}
64+
65+
public void putIfAbsent(K key, V value) {
66+
data.putIfAbsent(key, value);
67+
}
68+
5669
/**
5770
* Retrieves a value from the cache by key.
5871
*

0 commit comments

Comments
 (0)