Skip to content

Commit f9225cb

Browse files
committed
fix: sorted out application view rights for all cases
1 parent bd510b6 commit f9225cb

File tree

11 files changed

+258
-137
lines changed

11 files changed

+258
-137
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.lowcoder.domain.application.model;
2+
3+
public enum ApplicationRequestType {
4+
PUBLIC_TO_ALL,
5+
PUBLIC_TO_MARKETPLACE,
6+
AGENCY_PROFILE,
7+
}

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationRepository.java

+22-7
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,28 @@ public interface ApplicationRepository extends ReactiveMongoRepository<Applicati
4545
// this we do not need
4646
// Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsAndAgencyProfileIsAndIdIn(Boolean publicToMarketplace, Boolean agencyProfile, Collection<String> ids);
4747

48-
// Find all Public Applications
49-
Flux<Application> findByPublicToAllIsTrue();
50-
51-
// Find all Marketplace Apps
48+
/**
49+
* Filter public applications from list of supplied IDs
50+
*/
51+
Flux<Application> findByPublicToAllIsTrueAndIdIn(Collection<String> ids);
52+
53+
/**
54+
* Filter marketplace applications from list of supplied IDs
55+
*/
56+
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrueAndIdIn(Collection<String> ids);
57+
58+
/**
59+
* Filter agency applications from list of supplied IDs
60+
*/
61+
Flux<Application> findByPublicToAllIsTrueAndAgencyProfileIsTrueAndIdIn(Collection<String> ids);
62+
63+
/**
64+
* Find all marketplace applications
65+
*/
5266
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();
53-
54-
// Find all Agencies
67+
68+
/**
69+
* Find all agency applications
70+
*/
5571
Flux<Application> findByPublicToAllIsTrueAndAgencyProfileIsTrue();
56-
5772
}

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationService.java

+42-60
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.stream.Collectors;
88

99
import org.lowcoder.domain.application.model.Application;
10+
import org.lowcoder.domain.application.model.ApplicationRequestType;
1011
import org.lowcoder.domain.application.model.ApplicationStatus;
1112
import org.lowcoder.domain.application.repository.ApplicationRepository;
1213
import org.lowcoder.domain.permission.model.ResourceRole;
@@ -157,8 +158,6 @@ public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, Boo
157158

158159
return findById(applicationId)
159160

160-
// Falk: question - do we need Map<String, Object> applicationDsl = application.getEditingApplicationDSL(); and .editingApplicationDSL(applicationDsl) - or is .publicToMarketplace(publicToMarketplace).build(); enough?
161-
162161
.map(application -> {
163162

164163
Map<String, Object> applicationDsl = application.getEditingApplicationDSL();
@@ -204,81 +203,64 @@ public Mono<Boolean> setApplicationAsAgencyProfile(String applicationId, boolean
204203
return mongoUpsertHelper.updateById(application, applicationId);
205204
}
206205

207-
// getPublicApplicationIds /view - publicToAll check
208-
// getPublicMarketplaceApplicationIds / marketplace_view - publicToAll and publicToMarketplace check & isPrivateMarketplace check
209-
// getPublicAgencyProfileApplicationIds / agency_profile_view - publicToAll and agencyProfile check
210206

211-
// marketplace_view [anonymous] publicToAll and publicToMarketplace check & isPrivateMarketplace false -> OK
207+
@NonEmptyMono
208+
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
209+
public Mono<Set<String>> getFilteredPublicApplicationIds(ApplicationRequestType requestType, Collection<String> applicationIds, Boolean isAnonymous, Boolean isPrivateMarketplace) {
210+
211+
switch(requestType)
212+
{
213+
case PUBLIC_TO_ALL:
214+
return getPublicApplicationIds(applicationIds);
215+
case PUBLIC_TO_MARKETPLACE:
216+
return getPublicMarketplaceApplicationIds(applicationIds, isAnonymous, isPrivateMarketplace);
217+
case AGENCY_PROFILE:
218+
return getPublicAgencyApplicationIds(applicationIds);
219+
default:
220+
return Mono.empty();
221+
}
222+
}
212223

213-
// marketplace_view [anonymous] publicToAll and publicToMarketplace check & isPrivateMarketplace true -> NOT OK
214-
215-
// marketplace_view [LoggedIn] publicToAll and publicToMarketplace check & isPrivateMarketplace true -> OK
216-
// marketplace_view [LoggedIn] publicToAll and publicToMarketplace check & isPrivateMarketplace false -> OK
217-
218-
219-
// will be extended by EndpointType
220-
/*
221-
* if (EndpointType == view)
222-
* if (EndpointType == marketplace_view)
223-
* if (EndpointType == agency_profile_view)
224+
225+
/**
226+
* Find all public applications - doesn't matter if user is anonymous, because these apps are public
224227
*/
225-
226-
// is it needed?
227228
@NonEmptyMono
228229
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
229-
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds, Boolean isAnonymous, Boolean isPrivateMarketplace) {
230+
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds) {
230231

231-
return repository.findByPublicToAllIsTrue()
232+
return repository.findByPublicToAllIsTrueAndIdIn(applicationIds)
232233
.map(HasIdAndAuditing::getId)
233234
.collect(Collectors.toSet());
234235
}
235236

236-
// for Marketplaces
237+
238+
/**
239+
* Find all marketplace applications - filter based on whether user is anonymous and whether it's a private marketplace
240+
*/
237241
@NonEmptyMono
238242
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
239-
public Mono<Set<String>> getPublicMarketplaceApplicationIds(Collection<String> applicationIds, Boolean isAnonymous, Boolean isPrivateMarketplace) {
240-
241-
if(isAnonymous) {
242-
if(isPrivateMarketplace) {
243-
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue(false, false, applicationIds)
244-
.map(HasIdAndAuditing::getId)
245-
.collect(Collectors.toSet());
246-
} else {
247-
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue(true, false, applicationIds)
248-
.map(HasIdAndAuditing::getId)
249-
.collect(Collectors.toSet());
250-
}
251-
} else {
252-
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue(true, true, applicationIds)
253-
.map(HasIdAndAuditing::getId)
254-
.collect(Collectors.toSet());
255-
}
256-
257-
243+
public Mono<Set<String>> getPublicMarketplaceApplicationIds(Collection<String> applicationIds, boolean isAnonymous, boolean isPrivateMarketplace) {
244+
245+
if ((isAnonymous && !isPrivateMarketplace) || !isAnonymous)
246+
{
247+
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrueAndIdIn(applicationIds)
248+
.map(HasIdAndAuditing::getId)
249+
.collect(Collectors.toSet());
250+
}
251+
return Mono.empty();
258252
}
259253

260-
// for Agencies
254+
/**
255+
* Find all agency applications
256+
*/
261257
@NonEmptyMono
262258
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
263-
public Mono<Set<String>> getPublicAgencyApplicationIds(Collection<String> applicationIds, Boolean isAnonymous, Boolean isPrivateMarketplace) {
264-
265-
if(isAnonymous) {
266-
if(isPrivateMarketplace) {
267-
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsAndAgencyProfileIsAndIdIn(false, false, applicationIds)
268-
.map(HasIdAndAuditing::getId)
269-
.collect(Collectors.toSet());
270-
} else {
271-
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsAndAgencyProfileIsAndIdIn(true, false, applicationIds)
272-
.map(HasIdAndAuditing::getId)
273-
.collect(Collectors.toSet());
274-
}
275-
} else {
276-
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsOrAgencyProfileIsAndIdIn(true, true, applicationIds)
277-
.map(HasIdAndAuditing::getId)
278-
.collect(Collectors.toSet());
279-
}
280-
259+
public Mono<Set<String>> getPublicAgencyApplicationIds(Collection<String> applicationIds) {
281260

261+
return repository.findByPublicToAllIsTrueAndAgencyProfileIsTrueAndIdIn(applicationIds)
262+
.map(HasIdAndAuditing::getId)
263+
.collect(Collectors.toSet());
282264
}
283265

284266
public Flux<Application> findAll() {

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/service/ApplicationPermissionHandler.java

+36-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Set;
1616

1717
import org.lowcoder.domain.application.model.Application;
18+
import org.lowcoder.domain.application.model.ApplicationRequestType;
1819
import org.lowcoder.domain.application.service.ApplicationService;
1920
import org.lowcoder.domain.permission.model.ResourceAction;
2021
import org.lowcoder.domain.permission.model.ResourcePermission;
@@ -46,7 +47,7 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
4647
}
4748

4849
Set<String> applicationIds = newHashSet(resourceIds);
49-
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds, Boolean.TRUE, config.getMarketplace().isPrivateMode()),
50+
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds),
5051
templateSolution.getTemplateApplicationIds(applicationIds))
5152
.map(tuple -> {
5253
Set<String> publicAppIds = tuple.getT1();
@@ -61,7 +62,7 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
6162
(Collection<String> resourceIds, ResourceAction resourceAction) {
6263

6364
Set<String> applicationIds = newHashSet(resourceIds);
64-
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds, Boolean.FALSE, config.getMarketplace().isPrivateMode()),
65+
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds),
6566
templateSolution.getTemplateApplicationIds(applicationIds))
6667
.map(tuple -> {
6768
Set<String> publicAppIds = tuple.getT1();
@@ -70,7 +71,39 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
7071
});
7172
}
7273

73-
private List<ResourcePermission> getAnonymousUserPermission(String applicationId) {
74+
75+
@Override
76+
protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserApplicationPermissions(
77+
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType)
78+
{
79+
if (!ANONYMOUS_USER_ROLE.canDo(resourceAction)) {
80+
return Mono.just(emptyMap());
81+
}
82+
83+
Set<String> applicationIds = newHashSet(resourceIds);
84+
return Mono.zip(applicationService.getFilteredPublicApplicationIds(requestType, applicationIds, Boolean.TRUE, config.getMarketplace().isPrivateMode()),
85+
templateSolution.getTemplateApplicationIds(applicationIds))
86+
.map(tuple -> {
87+
Set<String> publicAppIds = tuple.getT1();
88+
Set<String> templateAppIds = tuple.getT2();
89+
return collectMap(union(publicAppIds, templateAppIds), identity(), this::getAnonymousUserPermission);
90+
});
91+
}
92+
93+
@Override
94+
protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserApplicationPublicResourcePermissions(
95+
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType) {
96+
Set<String> applicationIds = newHashSet(resourceIds);
97+
return Mono.zip(applicationService.getFilteredPublicApplicationIds(requestType, applicationIds, Boolean.FALSE, config.getMarketplace().isPrivateMode()),
98+
templateSolution.getTemplateApplicationIds(applicationIds))
99+
.map(tuple -> {
100+
Set<String> publicAppIds = tuple.getT1();
101+
Set<String> templateAppIds = tuple.getT2();
102+
return collectMap(union(publicAppIds, templateAppIds), identity(), this::getAnonymousUserPermission);
103+
});
104+
}
105+
106+
private List<ResourcePermission> getAnonymousUserPermission(String applicationId) {
74107
return Collections.singletonList(ResourcePermission.builder()
75108
.resourceId(applicationId)
76109
.resourceType(ResourceType.APPLICATION)

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/service/DatasourcePermissionHandler.java

+13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.stream.Collectors;
1111

1212
import org.apache.commons.collections4.CollectionUtils;
13+
import org.lowcoder.domain.application.model.ApplicationRequestType;
1314
import org.lowcoder.domain.datasource.model.Datasource;
1415
import org.lowcoder.domain.datasource.service.DatasourceService;
1516
import org.lowcoder.domain.permission.model.ResourceAction;
@@ -44,6 +45,18 @@ protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserPublicR
4445
}
4546

4647
@Override
48+
protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserApplicationPermissions(
49+
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType) {
50+
return Mono.just(Collections.emptyMap());
51+
}
52+
53+
@Override
54+
protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserApplicationPublicResourcePermissions(
55+
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType) {
56+
return Mono.just(Collections.emptyMap());
57+
}
58+
59+
@Override
4760
protected Mono<String> getOrgId(String resourceId) {
4861
return datasourceService.getById(resourceId)
4962
.map(Datasource::getOrganizationId);

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/service/ResourcePermissionHandler.java

+67
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.apache.commons.collections4.CollectionUtils;
2020
import org.apache.commons.collections4.ListUtils;
21+
import org.lowcoder.domain.application.model.ApplicationRequestType;
2122
import org.lowcoder.domain.group.service.GroupMemberService;
2223
import org.lowcoder.domain.organization.service.OrgMemberService;
2324
import org.lowcoder.domain.permission.model.ResourceAction;
@@ -153,6 +154,13 @@ protected abstract Mono<Map<String, List<ResourcePermission>>> getAnonymousUserP
153154
protected abstract Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserPublicResourcePermissions
154155
(Collection<String> resourceIds, ResourceAction resourceAction);
155156

157+
protected abstract Mono<Map<String, List<ResourcePermission>>> getAnonymousUserApplicationPermissions(Collection<String> resourceIds,
158+
ResourceAction resourceAction, ApplicationRequestType requestType);
159+
160+
protected abstract Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserApplicationPublicResourcePermissions
161+
(Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType);
162+
163+
156164
private Mono<Map<String, List<ResourcePermission>>> getAllMatchingPermissions0(String userId, String orgId, ResourceType resourceType,
157165
Collection<String> resourceIds,
158166
ResourceAction resourceAction) {
@@ -212,4 +220,63 @@ private Mono<Set<String>> getUserGroupIds(String orgId, String userId) {
212220
}
213221

214222
protected abstract Mono<String> getOrgId(String resourceId);
223+
224+
public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnApplication(String userId, String resourceId,
225+
ResourceAction resourceAction, ApplicationRequestType requestType)
226+
{
227+
ResourceType resourceType = resourceAction.getResourceType();
228+
229+
Mono<UserPermissionOnResourceStatus> publicResourcePermissionMono = getAnonymousUserApplicationPermissions(singletonList(resourceId), resourceAction, requestType)
230+
.map(it -> it.getOrDefault(resourceId, emptyList()))
231+
.map(it -> {
232+
if (!it.isEmpty()) {
233+
return UserPermissionOnResourceStatus.success(it.get(0));
234+
}
235+
return isAnonymousUser(userId) ? UserPermissionOnResourceStatus.anonymousUser() : UserPermissionOnResourceStatus.notInOrg();
236+
});
237+
238+
if (isAnonymousUser(userId)) {
239+
return publicResourcePermissionMono;
240+
}
241+
242+
Mono<UserPermissionOnResourceStatus> nonAnonymousPublicResourcePermissionMono = getNonAnonymousUserApplicationPublicResourcePermissions(singletonList(resourceId), resourceAction, requestType)
243+
.map(it -> it.getOrDefault(resourceId, emptyList()))
244+
.map(it -> {
245+
if (!it.isEmpty()) {
246+
return UserPermissionOnResourceStatus.success(it.get(0));
247+
}
248+
return isAnonymousUser(userId) ? UserPermissionOnResourceStatus.anonymousUser() : UserPermissionOnResourceStatus.notInOrg();
249+
});
250+
251+
252+
Mono<UserPermissionOnResourceStatus> orgUserPermissionMono = getOrgId(resourceId)
253+
.flatMap(orgId -> orgMemberService.getOrgMember(orgId, userId))
254+
.flatMap(orgMember -> {
255+
if (orgMember.isAdmin()) {
256+
return Mono.just(UserPermissionOnResourceStatus.success(buildAdminPermission(resourceType, resourceId, userId)));
257+
}
258+
return getAllMatchingPermissions0(userId, orgMember.getOrgId(), resourceType, Collections.singleton(resourceId), resourceAction)
259+
.map(it -> it.getOrDefault(resourceId, emptyList()))
260+
.map(permissions -> permissions.isEmpty() ? UserPermissionOnResourceStatus.notEnoughPermission()
261+
: UserPermissionOnResourceStatus.success(getMaxPermission(permissions)));
262+
})
263+
.defaultIfEmpty(UserPermissionOnResourceStatus.notInOrg());
264+
265+
return Mono.zip(publicResourcePermissionMono, nonAnonymousPublicResourcePermissionMono, orgUserPermissionMono)
266+
.map(tuple -> {
267+
UserPermissionOnResourceStatus publicResourcePermission = tuple.getT1();
268+
UserPermissionOnResourceStatus nonAnonymousPublicResourcePermission = tuple.getT2();
269+
UserPermissionOnResourceStatus orgUserPermission = tuple.getT3();
270+
if (orgUserPermission.hasPermission()) {
271+
return orgUserPermission;
272+
}
273+
if(nonAnonymousPublicResourcePermission.hasPermission()) {
274+
return nonAnonymousPublicResourcePermission;
275+
}
276+
if (publicResourcePermission.hasPermission()) {
277+
return publicResourcePermission;
278+
}
279+
return orgUserPermission;
280+
});
281+
}
215282
}

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/service/ResourcePermissionService.java

+9
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import javax.validation.constraints.NotNull;
2020

2121
import org.apache.commons.collections4.CollectionUtils;
22+
import org.lowcoder.domain.application.model.ApplicationRequestType;
2223
import org.lowcoder.domain.permission.model.ResourceAction;
2324
import org.lowcoder.domain.permission.model.ResourceHolder;
2425
import org.lowcoder.domain.permission.model.ResourcePermission;
@@ -221,6 +222,14 @@ public Mono<ResourcePermission> checkAndReturnMaxPermission(String userId, Strin
221222
return resourcePermissionHandler.checkUserPermissionStatusOnResource(userId, resourceId, resourceAction);
222223
}
223224

225+
public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnApplication
226+
(String userId, String resourceId, ResourceAction resourceAction, ApplicationRequestType requestType) {
227+
ResourceType resourceType = resourceAction.getResourceType();
228+
var resourcePermissionHandler = getResourcePermissionHandler(resourceType);
229+
return resourcePermissionHandler.checkUserPermissionStatusOnApplication(userId, resourceId, resourceAction, requestType);
230+
}
231+
232+
224233
public Mono<Boolean> removeUserApplicationPermission(String appId, String userId) {
225234
return repository.removePermissionBy(ResourceType.APPLICATION, appId, ResourceHolder.USER, userId);
226235
}

0 commit comments

Comments
 (0)