Skip to content

Commit 08832c0

Browse files
author
Gerald Unterrainer
committed
try tenants
1 parent 9b256c8 commit 08832c0

File tree

7 files changed

+159
-49
lines changed

7 files changed

+159
-49
lines changed

Diff for: src/main/java/info/unterrainer/commons/httpserver/accessmanager/HttpAccessManager.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,13 @@ private TokenVerifier<AccessToken> persistUserInfoInContext(final Context ctx) {
169169
ctx.attribute(Attribute.USER_EMAIL_VERIFIED, token.getEmailVerified());
170170
ctx.attribute(Attribute.USER_REALM_ROLES, token.getRealmAccess().getRoles());
171171

172-
String tenant = (String) token.getOtherClaims().get("tenant");
173-
ctx.attribute(Attribute.USER_CLIENT_ATTRIBUTE_TENANT, tenant);
174-
ctx.attribute(Attribute.USER_TENANT_SET, createTenantSetFrom(tenant));
172+
String readTenants = (String) token.getOtherClaims().get("tenants_read");
173+
ctx.attribute(Attribute.USER_CLIENT_ATTRIBUTE_TENANTS_READ, readTenants);
174+
ctx.attribute(Attribute.USER_TENANTS_READ_SET, createTenantSetFrom(readTenants));
175+
176+
String writeTenants = (String) token.getOtherClaims().get("tenants_write");
177+
ctx.attribute(Attribute.USER_CLIENT_ATTRIBUTE_TENANTS_WRITE, writeTenants);
178+
ctx.attribute(Attribute.USER_TENANTS_WRITE_SET, createTenantSetFrom(writeTenants));
175179

176180
Set<String> clientRoles = Set.of();
177181
String key = token.getIssuedFor();
@@ -190,7 +194,7 @@ private TokenVerifier<AccessToken> persistUserInfoInContext(final Context ctx) {
190194
.email(token.getEmail())
191195
.emailVerified(token.getEmailVerified())
192196
.realmRoles(token.getRealmAccess().getRoles())
193-
.tenant(tenant)
197+
.tenant(readTenants)
194198
.clientRoles(clientRoles)
195199
.isActive(token.isActive())
196200
.isBearer(token.getType().equalsIgnoreCase("bearer"))
@@ -218,15 +222,19 @@ private TokenVerifier<AccessToken> persistUserInfoInContext(final Context ctx) {
218222
}
219223

220224
private Object createTenantSetFrom(final String tenant) {
221-
Set<String> tenantSet = new HashSet<>();
225+
Set<Long> tenantSet = new HashSet<>();
222226
if (tenant == null || tenant.isBlank())
223227
return tenantSet;
224228

225229
String[] tenants = tenant.split(",");
226230
for (String t : tenants) {
227231
if (t.isBlank())
228232
continue;
229-
tenantSet.add(t.trim());
233+
try {
234+
tenantSet.add(Long.parseLong(t.trim()));
235+
} catch (NumberFormatException e) {
236+
// NOOP
237+
}
230238
}
231239
return tenantSet;
232240
}

Diff for: src/main/java/info/unterrainer/commons/httpserver/daos/BasicListQueryBuilder.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
import lombok.RequiredArgsConstructor;
1313

1414
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
15-
public class BasicListQueryBuilder<P extends BasicJpa, T, R extends BasicListQueryBuilder<P, T, R>>
16-
extends BasicQueryGeneralBuilder<P, T, R> {
15+
public class BasicListQueryBuilder<P extends BasicJpa, X, R extends BasicListQueryBuilder<P, X, R>>
16+
extends BasicQueryGeneralBuilder<P, X, R> {
1717

1818
protected final EntityManagerFactory emf;
1919
@Getter
2020
protected final BasicJpqlDao<P> dao;
21-
protected final Class<T> resultType;
21+
protected final Class<X> resultType;
2222

2323
protected String selectClause = "o";
2424
protected String orderByClause;
@@ -30,12 +30,12 @@ void setSelect(final String selectClause) {
3030
this.selectClause = "o";
3131
}
3232

33-
public TypedQuery<T> getTypedQuery(final EntityManager em) {
33+
public TypedQuery<X> getTypedQuery(final EntityManager em) {
3434
return dao.coreDao.getQuery(em, selectClause, joinClause, whereClause, parameters, resultType, orderByClause,
3535
lockPessimistic, null);
3636
}
3737

38-
public TypedQuery<T> getDeleteQuery(final EntityManager em) {
38+
public TypedQuery<X> getDeleteQuery(final EntityManager em) {
3939
return dao.coreDao.getDeleteQuery(em, joinClause, whereClause, parameters);
4040
}
4141

Diff for: src/main/java/info/unterrainer/commons/httpserver/daos/BasicQueryEntityManagerBuilder.java

+39
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package info.unterrainer.commons.httpserver.daos;
22

3+
import java.util.Arrays;
4+
import java.util.HashSet;
5+
import java.util.Set;
6+
37
import javax.persistence.EntityManager;
48

9+
import org.eclipse.jetty.webapp.WebAppContext.Context;
10+
11+
import info.unterrainer.commons.httpserver.enums.Attribute;
512
import info.unterrainer.commons.rdbutils.entities.BasicJpa;
613
import lombok.AccessLevel;
714
import lombok.Getter;
@@ -12,6 +19,8 @@ public class BasicQueryEntityManagerBuilder<P extends BasicJpa, T, R extends Bas
1219

1320
@Getter
1421
protected EntityManager entityManager;
22+
protected Set<Long> tenantIds;
23+
protected Set<Long> writeTenantIds;
1524

1625
/**
1726
* Sets a custom {@link EntityManager}.
@@ -27,4 +36,34 @@ public R entityManager(final EntityManager em) {
2736
entityManager = em;
2837
return (R) this;
2938
}
39+
40+
/**
41+
* Set custom tenant-IDs to be used when querying / writing to the database.
42+
* <p>
43+
* Overwrites the existing set.
44+
*
45+
* @param id the tenant-ID to use
46+
* @return an instance of this builder to provide a fluent interface
47+
*/
48+
@SuppressWarnings("unchecked")
49+
public R tenant(final Long... ids) {
50+
tenantIds = new HashSet<>(Arrays.asList(ids));
51+
return (R) this;
52+
}
53+
54+
/**
55+
* Set a custom tenant-ID that is retrieved from the given context to be used
56+
* when querying the database.
57+
* <p>
58+
* Overwrites the existing set.
59+
*
60+
* @param ctx the context that contains the tenant-ID to use
61+
* @return an instance of this builder to provide a fluent interface
62+
*/
63+
@SuppressWarnings("unchecked")
64+
public R tenant(final Context ctx) {
65+
tenantIds = (Set<Long>) ctx.getAttribute(Attribute.USER_TENANTS_READ_SET);
66+
writeTenantIds = (Set<Long>) ctx.getAttribute(Attribute.USER_TENANTS_WRITE_SET);
67+
return (R) this;
68+
}
3069
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
package info.unterrainer.commons.httpserver.daos;
22

3+
import java.util.Set;
4+
35
import info.unterrainer.commons.httpserver.jsons.ListJson;
46
import info.unterrainer.commons.rdbutils.entities.BasicJpa;
57

68
public interface CoreDao<P extends BasicJpa, E> {
79

10+
TenantData getTenantData();
11+
812
DaoTransactionManager<E> getTransactionManager();
913

10-
P create(E manager, P mappedJpa);
14+
P create(E manager, P mappedJpa, final Set<Long> tenantIds);
1115

12-
P getById(E manager, Long id);
16+
P getById(E manager, Long id, final Set<Long> tenantIds);
1317

1418
ListJson<P> getList(E manager, Long offset, Long size, String selectClause, String joinClause, String whereClause,
15-
ParamMap params, String orderByClause);
19+
ParamMap params, String orderByClause, final Set<Long> tenantIds);
1620

17-
void delete(E manager, Long id);
21+
void delete(E manager, Long id, final Set<Long> tenantIds);
1822

19-
P update(E manager, P mappedJpa);
23+
P update(E manager, P mappedJpa, final Set<Long> tenantIds);
2024
}

Diff for: src/main/java/info/unterrainer/commons/httpserver/daos/JpqlCoreDao.java

+87-29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package info.unterrainer.commons.httpserver.daos;
22

33
import java.time.LocalDateTime;
4+
import java.util.HashMap;
45
import java.util.List;
56
import java.util.Map;
67
import java.util.Map.Entry;
@@ -25,6 +26,8 @@ public class JpqlCoreDao<P extends BasicJpa> implements CoreDao<P, EntityManager
2526
protected final Class<P> type;
2627
@Getter
2728
protected JpqlTransactionManager transactionManager;
29+
@Getter
30+
protected TenantData tenantData;
2831

2932
public JpqlCoreDao(final EntityManagerFactory emf, final Class<P> type) {
3033
super();
@@ -34,64 +37,75 @@ public JpqlCoreDao(final EntityManagerFactory emf, final Class<P> type) {
3437
}
3538

3639
@Override
37-
public P create(final EntityManager em, final P entity) {
40+
public P create(final EntityManager em, final P entity, final Set<Long> tenantIds) {
3841
LocalDateTime time = DateUtils.nowUtc();
42+
43+
if (hasTenantData())create entries...
44+
3945
entity.setCreatedOn(time);
4046
entity.setEditedOn(time);
4147
em.persist(entity);
4248
return entity;
4349
}
4450

4551
@Override
46-
public void delete(final EntityManager em, final Long id) {
47-
em.createQuery(String.format("DELETE FROM %s AS o WHERE o.id = :id", type.getSimpleName()))
48-
.setParameter("id", id)
49-
.executeUpdate();
52+
public void delete(final EntityManager em, final Long id, final Set<Long> tenantIds) {
53+
Map<String, Object> params = new HashMap<>();
54+
params.put("id", id);
55+
params = addTenantParams(params, tenantIds);
56+
Query query = em.createQuery(
57+
String.format("DELETE FROM %s AS o " + addTenantJoin(null) + " WHERE " + addTenantWhere("o.id = :id"),
58+
type.getSimpleName()));
59+
for (Entry<String, Object> entry : params.entrySet())
60+
query.setParameter(entry.getKey(), entry.getValue());
61+
query.executeUpdate();
5062
}
5163

5264
@Override
53-
public P getById(final EntityManager em, final Long id) {
65+
public P getById(final EntityManager em, final Long id, final Set<Long> tenantIds) {
5466
try {
55-
return getQuery(em, "o", null, "o.id = :id", Map.of("id", id), type, null, false, null).getSingleResult();
67+
return getQuery(em, "o", null, "o.id = :id", Map.of("id", id), type, null, false, null, tenantIds)
68+
.getSingleResult();
5669
} catch (NoResultException e) {
5770
return null;
5871
}
5972
}
6073

6174
@Override
6275
public ListJson<P> getList(final EntityManager em, final Long offset, final Long size, final String selectClause,
63-
final String joinClause, final String whereClause, final ParamMap params, final String orderByClause) {
76+
final String joinClause, final String whereClause, final ParamMap params, final String orderByClause,
77+
final Set<Long> tenantIds) {
6478
ListJson<P> r = new ListJson<>();
65-
r.setEntries(
66-
getList(em,
67-
getQuery(em, selectClause, joinClause, whereClause,
68-
params == null ? null : params.getParameters(), type, orderByClause, false, null),
69-
offset, size));
79+
r.setEntries(getList(em, getQuery(em, selectClause, joinClause, whereClause,
80+
params == null ? null : params.getParameters(), type, orderByClause, false, null, tenantIds), offset,
81+
size));
7082
r.setCount((Long) getCountQuery(em, selectClause, joinClause, whereClause,
71-
params == null ? null : params.getParameters(), null).getSingleResult());
83+
params == null ? null : params.getParameters(), null, tenantIds).getSingleResult());
7284
return r;
7385
}
7486

7587
@Override
76-
public P update(final EntityManager em, final P entity) {
88+
public P update(final EntityManager em, final P entity, final Set<Long> tenantIds) {
7789
LocalDateTime time = DateUtils.nowUtc();
7890
entity.setEditedOn(time);
7991
return em.merge(entity);
8092
}
8193

82-
<T> TypedQuery<T> getQuery(final EntityManager em, final String selectClause, final String joinClause,
83-
final String whereClause, final Map<String, Object> params, final Class<T> type, final String orderBy,
84-
final boolean lockPessimistic, final Set<AsyncState> asyncStates) {
94+
<T> TypedQuery<T> getQuery(final EntityManager em, final String selectClause, String joinClause, String whereClause,
95+
Map<String, Object> params, final Class<T> type, final String orderBy, final boolean lockPessimistic,
96+
final Set<AsyncState> asyncStates, final Set<Long> tenantIds) {
8597
String query = "SELECT ";
8698
if (selectClause == null || selectClause.isBlank())
8799
query += "o";
88100
else
89101
query += selectClause;
90102
query += " FROM %s AS o";
91103

104+
joinClause = addTenantJoin(joinClause);
92105
if (joinClause != null && !joinClause.isBlank())
93106
query += " " + joinClause;
94107

108+
whereClause = addTenantWhere(whereClause);
95109
query += buildWhereClause(whereClause, asyncStates);
96110

97111
if (orderBy == null)
@@ -110,20 +124,25 @@ else if (!orderBy.isBlank())
110124
if (lockPessimistic)
111125
q.setLockMode(LockModeType.PESSIMISTIC_WRITE);
112126
q = addAsyncStatesParamsToQuery(asyncStates, q);
127+
128+
params = addTenantParams(params, tenantIds);
113129
if (params != null)
114130
for (Entry<String, Object> e : params.entrySet())
115131
q.setParameter(e.getKey(), e.getValue());
116132
return q;
117133
}
118134

119-
<T> TypedQuery<T> getDeleteQuery(final EntityManager em, final String joinClause, final String whereClause,
120-
final Map<String, Object> params) {
135+
<T> TypedQuery<T> getDeleteQuery(final EntityManager em, String joinClause, String whereClause,
136+
Map<String, Object> params, final Set<Long> tenantIds) {
121137
String query = "DELETE FROM %s AS o";
122138

139+
joinClause = addTenantJoin(joinClause);
123140
if (joinClause != null && !joinClause.isBlank())
124141
query += " " + joinClause;
125142

143+
whereClause = addTenantWhere(whereClause);
126144
query += buildWhereClause(whereClause, null);
145+
params = addTenantParams(params, tenantIds);
127146

128147
query = String.format(query, this.type.getSimpleName());
129148

@@ -137,17 +156,22 @@ <T> TypedQuery<T> getDeleteQuery(final EntityManager em, final String joinClause
137156
return q;
138157
}
139158

140-
Query getCountQuery(final EntityManager em, final String selectClause, final String joinClause,
141-
final String whereClause, final Map<String, Object> params, final Set<AsyncState> asyncStates) {
159+
Query getCountQuery(final EntityManager em, final String selectClause, String joinClause, String whereClause,
160+
Map<String, Object> params, final Set<AsyncState> asyncStates, final Set<Long> tenantIds) {
142161
String query = "SELECT COUNT(";
143162
if (selectClause == null || selectClause.isBlank())
144163
query += "o.id";
145164
else
146165
query += selectClause;
147166
query += ") FROM %s AS o";
167+
168+
joinClause = addTenantJoin(joinClause);
148169
if (joinClause != null && !joinClause.isBlank())
149170
query += " " + joinClause;
171+
172+
whereClause = addTenantWhere(whereClause);
150173
query += buildWhereClause(whereClause, asyncStates);
174+
params = addTenantParams(params, tenantIds);
151175

152176
Query q = em.createQuery(String.format(query, this.type.getSimpleName()));
153177

@@ -170,18 +194,52 @@ <T> List<T> getList(final EntityManager em, final TypedQuery<T> query, final lon
170194
return query.getResultList();
171195
}
172196

173-
private boolean isAllowed(final EntityManager em, final TenantData tenantData) {
174-
TypedQuery<Long> query = getQuery(em, "o.id",
175-
"RIGHT JOIN " + tenantData.getJpa().getClass().getSimpleName() + " tenantTable on o.id = tenantTable."
176-
+ tenantData.getReferenceField(),
177-
"tenantTable." + tenantData.getReferenceField() + " IS NULL OR tenantTable." + tenantData.getIdField()
178-
+ " = :tenantId",
179-
null, Long.class, null, false, null);
197+
private boolean isAllowed(final EntityManager em, final Set<Long> tenantIds) {
198+
if (!hasTenantData() || tenantIds == null || tenantIds.contains(null))
199+
return true;
200+
TypedQuery<Long> query = getQuery(em, "o.id", null, null, null, Long.class, null, false, null, tenantIds);
180201
query.setMaxResults(1);
181202
List<Long> list = query.getResultList();
182203
return list != null && list.size() > 0;
183204
}
184205

206+
private String addTenantJoin(final String joinClause) {
207+
if (!hasTenantData())
208+
return joinClause;
209+
210+
String r = joinClause;
211+
if (joinClause == null || joinClause.isBlank())
212+
r = "";
213+
214+
r += String.format(" LEFT JOIN %s tenantTable on o.id = tenantTable.%s", tenantData.getType().getSimpleName(),
215+
tenantData.getReferenceField());
216+
return r;
217+
}
218+
219+
private String addTenantWhere(final String whereClause) {
220+
if (!hasTenantData())
221+
return whereClause;
222+
223+
String r = "";
224+
if (whereClause != null && !whereClause.isBlank())
225+
r = "( " + whereClause + ") AND ";
226+
227+
r += String.format("( tenantTable.%1$s IS NULL OR tenantTable.%1$s IN (:tenantIds) )", tenantData.getIdField());
228+
return r;
229+
}
230+
231+
private Map<String, Object> addTenantParams(final Map<String, Object> map, final Set<Long> tenantIds) {
232+
if (!hasTenantData() || tenantIds.isEmpty())
233+
return map;
234+
map.put("tenantIds", tenantIds);
235+
return map;
236+
}
237+
238+
private boolean hasTenantData() {
239+
return tenantData != null && tenantData.getType() != null && tenantData.getReferenceField() != null
240+
&& tenantData.getIdField() != null;
241+
}
242+
185243
private boolean isSet(final String str) {
186244
return str != null && !str.isBlank();
187245
}

0 commit comments

Comments
 (0)