Skip to content

Solve the marketplace #735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,8 @@ public Object getLiveContainerSize() {
return liveContainerSize.get();
}

public Map<String, Object> getPublishedApplicationDSL() {
return publishedApplicationDSL;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.lowcoder.domain.application.model;

public enum ApplicationRequestType {
PUBLIC_TO_ALL,
PUBLIC_TO_MARKETPLACE,
AGENCY_PROFILE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@


import java.util.Collection;
import java.util.List;

import javax.annotation.Nonnull;

Expand All @@ -18,6 +17,7 @@
@Repository
public interface ApplicationRepository extends ReactiveMongoRepository<Application, String>, CustomApplicationRepository {

// publishedApplicationDSL : 0 -> excludes publishedApplicationDSL from the return
@Query(fields = "{ publishedApplicationDSL : 0 , editingApplicationDSL : 0 }")
Flux<Application> findByOrganizationId(String organizationId);

Expand All @@ -32,19 +32,30 @@ public interface ApplicationRepository extends ReactiveMongoRepository<Applicati
@Query("{$or : [{'publishedApplicationDSL.queries.datasourceId':?0},{'editingApplicationDSL.queries.datasourceId':?0}]}")
Flux<Application> findByDatasourceId(String datasourceId);

Flux<Application> findByIdIn(List<String> ids);
Flux<Application> findByIdIn(Collection<String> ids);

/**
* Filter public applications from list of supplied IDs
*/
Flux<Application> findByPublicToAllIsTrueAndIdIn(Collection<String> ids);

// Falk: Why to combine? Marketplace-List and Agency-List are different Endpoints
/**
* Filter marketplace applications from list of supplied IDs
*/
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrueAndIdIn(Collection<String> ids);

@Query(value = "{$and:[{'publicToAll':true},{'$or':[{'publicToMarketplace':?0},{'agencyProfile':?1}]}, {'_id': { $in: ?2}}]}", fields = "{_id : 1}")
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsOrAgencyProfileIsAndIdIn
(Boolean publicToMarketplace, Boolean agencyProfile, Collection<String> ids);

Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsAndAgencyProfileIsAndIdIn(Boolean publicToMarketplace, Boolean agencyProfile, Collection<String> ids);
/**
* Filter agency applications from list of supplied IDs
*/
Flux<Application> findByPublicToAllIsTrueAndAgencyProfileIsTrueAndIdIn(Collection<String> ids);

/**
* Find all marketplace applications
*/
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();


/**
* Find all agency applications
*/
Flux<Application> findByPublicToAllIsTrueAndAgencyProfileIsTrue();

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.stream.Collectors;

import org.lowcoder.domain.application.model.Application;
import org.lowcoder.domain.application.model.ApplicationRequestType;
import org.lowcoder.domain.application.model.ApplicationStatus;
import org.lowcoder.domain.application.repository.ApplicationRepository;
import org.lowcoder.domain.permission.model.ResourceRole;
Expand Down Expand Up @@ -157,8 +158,6 @@ public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, Boo

return findById(applicationId)

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

.map(application -> {

Map<String, Object> applicationDsl = application.getEditingApplicationDSL();
Expand Down Expand Up @@ -204,27 +203,84 @@ public Mono<Boolean> setApplicationAsAgencyProfile(String applicationId, boolean
return mongoUpsertHelper.updateById(application, applicationId);
}


@NonEmptyMono
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
public Mono<Set<String>> getFilteredPublicApplicationIds(ApplicationRequestType requestType, Collection<String> applicationIds, boolean isAnonymous, Boolean isPrivateMarketplace) {

switch(requestType)
{
case PUBLIC_TO_ALL:
if (isAnonymous)
{
return getPublicApplicationIds(applicationIds);
}
else
{
return getPrivateApplicationIds(applicationIds);
}
case PUBLIC_TO_MARKETPLACE:
return getPublicMarketplaceApplicationIds(applicationIds, isAnonymous, isPrivateMarketplace);
case AGENCY_PROFILE:
return getPublicAgencyApplicationIds(applicationIds);
default:
return Mono.empty();
}
}


/**
* Find all public applications - doesn't matter if user is anonymous, because these apps are public
*/
@NonEmptyMono
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds, Boolean isAnonymous, Boolean isPrivateMarketplace) {
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds) {

if(isAnonymous) {
if(isPrivateMarketplace) {
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsAndAgencyProfileIsAndIdIn(false, false, applicationIds)
return repository.findByPublicToAllIsTrueAndIdIn(applicationIds)
.map(HasIdAndAuditing::getId)
.collect(Collectors.toSet());
} else {
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsAndAgencyProfileIsAndIdIn(true, false, applicationIds)
.map(HasIdAndAuditing::getId)
.collect(Collectors.toSet());
}
} else {
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsOrAgencyProfileIsAndIdIn(true, true, applicationIds)
}


/**
* Find all private applications for viewing.
*/
@NonEmptyMono
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
public Mono<Set<String>> getPrivateApplicationIds(Collection<String> applicationIds) {
// TODO: in 2.4.0 we need to check whether the app was published or not
return repository.findByIdIn(applicationIds)
.map(HasIdAndAuditing::getId)
.collect(Collectors.toSet());
}
}


/**
* Find all marketplace applications - filter based on whether user is anonymous and whether it's a private marketplace
*/
@NonEmptyMono
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
public Mono<Set<String>> getPublicMarketplaceApplicationIds(Collection<String> applicationIds, boolean isAnonymous, boolean isPrivateMarketplace) {

if ((isAnonymous && !isPrivateMarketplace) || !isAnonymous)
{
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrueAndIdIn(applicationIds)
.map(HasIdAndAuditing::getId)
.collect(Collectors.toSet());
}
return Mono.empty();
}

/**
* Find all agency applications
*/
@NonEmptyMono
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
public Mono<Set<String>> getPublicAgencyApplicationIds(Collection<String> applicationIds) {

return repository.findByPublicToAllIsTrueAndAgencyProfileIsTrueAndIdIn(applicationIds)
.map(HasIdAndAuditing::getId)
.collect(Collectors.toSet());
}

public Flux<Application> findAll() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.lowcoder.domain.application.model.Application;
import org.lowcoder.domain.application.model.ApplicationRequestType;
import org.lowcoder.domain.application.service.ApplicationService;
import org.lowcoder.domain.permission.model.ResourceAction;
import org.lowcoder.domain.permission.model.ResourcePermission;
Expand Down Expand Up @@ -46,7 +48,7 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
}

Set<String> applicationIds = newHashSet(resourceIds);
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds, Boolean.TRUE, config.getMarketplace().isPrivateMode()),
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds),
templateSolution.getTemplateApplicationIds(applicationIds))
.map(tuple -> {
Set<String> publicAppIds = tuple.getT1();
Expand All @@ -61,7 +63,7 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
(Collection<String> resourceIds, ResourceAction resourceAction) {

Set<String> applicationIds = newHashSet(resourceIds);
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds, Boolean.FALSE, config.getMarketplace().isPrivateMode()),
return Mono.zip(applicationService.getPrivateApplicationIds(applicationIds),
templateSolution.getTemplateApplicationIds(applicationIds))
.map(tuple -> {
Set<String> publicAppIds = tuple.getT1();
Expand All @@ -70,7 +72,41 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
});
}

private List<ResourcePermission> getAnonymousUserPermission(String applicationId) {

@Override
protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserApplicationPermissions(
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType)
{
if (!ANONYMOUS_USER_ROLE.canDo(resourceAction)) {
return Mono.just(emptyMap());
}

Set<String> applicationIds = newHashSet(resourceIds);
return Mono.zip(applicationService.getFilteredPublicApplicationIds(requestType, applicationIds, Boolean.TRUE, config.getMarketplace().isPrivateMode())
.defaultIfEmpty(new HashSet<>()),
templateSolution.getTemplateApplicationIds(applicationIds)
.defaultIfEmpty(new HashSet<>())
).map(tuple -> {
Set<String> publicAppIds = tuple.getT1();
Set<String> templateAppIds = tuple.getT2();
return collectMap(union(publicAppIds, templateAppIds), identity(), this::getAnonymousUserPermission);
});
}

@Override
protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserApplicationPublicResourcePermissions(
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType) {
Set<String> applicationIds = newHashSet(resourceIds);
return Mono.zip(applicationService.getFilteredPublicApplicationIds(requestType, applicationIds, Boolean.FALSE, config.getMarketplace().isPrivateMode()),
templateSolution.getTemplateApplicationIds(applicationIds))
.map(tuple -> {
Set<String> publicAppIds = tuple.getT1();
Set<String> templateAppIds = tuple.getT2();
return collectMap(union(publicAppIds, templateAppIds), identity(), this::getAnonymousUserPermission);
});
}

private List<ResourcePermission> getAnonymousUserPermission(String applicationId) {
return Collections.singletonList(ResourcePermission.builder()
.resourceId(applicationId)
.resourceType(ResourceType.APPLICATION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.lowcoder.domain.application.model.ApplicationRequestType;
import org.lowcoder.domain.datasource.model.Datasource;
import org.lowcoder.domain.datasource.service.DatasourceService;
import org.lowcoder.domain.permission.model.ResourceAction;
Expand Down Expand Up @@ -44,6 +45,18 @@ protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserPublicR
}

@Override
protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserApplicationPermissions(
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType) {
return Mono.just(Collections.emptyMap());
}

@Override
protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserApplicationPublicResourcePermissions(
Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType) {
return Mono.just(Collections.emptyMap());
}

@Override
protected Mono<String> getOrgId(String resourceId) {
return datasourceService.getById(resourceId)
.map(Datasource::getOrganizationId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.lowcoder.domain.application.model.ApplicationRequestType;
import org.lowcoder.domain.group.service.GroupMemberService;
import org.lowcoder.domain.organization.service.OrgMemberService;
import org.lowcoder.domain.permission.model.ResourceAction;
Expand Down Expand Up @@ -153,6 +154,13 @@ protected abstract Mono<Map<String, List<ResourcePermission>>> getAnonymousUserP
protected abstract Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserPublicResourcePermissions
(Collection<String> resourceIds, ResourceAction resourceAction);

protected abstract Mono<Map<String, List<ResourcePermission>>> getAnonymousUserApplicationPermissions(Collection<String> resourceIds,
ResourceAction resourceAction, ApplicationRequestType requestType);

protected abstract Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserApplicationPublicResourcePermissions
(Collection<String> resourceIds, ResourceAction resourceAction, ApplicationRequestType requestType);


private Mono<Map<String, List<ResourcePermission>>> getAllMatchingPermissions0(String userId, String orgId, ResourceType resourceType,
Collection<String> resourceIds,
ResourceAction resourceAction) {
Expand Down Expand Up @@ -212,4 +220,63 @@ private Mono<Set<String>> getUserGroupIds(String orgId, String userId) {
}

protected abstract Mono<String> getOrgId(String resourceId);

public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnApplication(String userId, String resourceId,
ResourceAction resourceAction, ApplicationRequestType requestType)
{
ResourceType resourceType = resourceAction.getResourceType();

Mono<UserPermissionOnResourceStatus> publicResourcePermissionMono = getAnonymousUserApplicationPermissions(singletonList(resourceId), resourceAction, requestType)
.map(it -> it.getOrDefault(resourceId, emptyList()))
.map(it -> {
if (!it.isEmpty()) {
return UserPermissionOnResourceStatus.success(it.get(0));
}
return isAnonymousUser(userId) ? UserPermissionOnResourceStatus.anonymousUser() : UserPermissionOnResourceStatus.notInOrg();
});

if (isAnonymousUser(userId)) {
return publicResourcePermissionMono;
}

Mono<UserPermissionOnResourceStatus> nonAnonymousPublicResourcePermissionMono = getNonAnonymousUserApplicationPublicResourcePermissions(singletonList(resourceId), resourceAction, requestType)
.map(it -> it.getOrDefault(resourceId, emptyList()))
.map(it -> {
if (!it.isEmpty()) {
return UserPermissionOnResourceStatus.success(it.get(0));
}
return isAnonymousUser(userId) ? UserPermissionOnResourceStatus.anonymousUser() : UserPermissionOnResourceStatus.notInOrg();
});


Mono<UserPermissionOnResourceStatus> orgUserPermissionMono = getOrgId(resourceId)
.flatMap(orgId -> orgMemberService.getOrgMember(orgId, userId))
.flatMap(orgMember -> {
if (orgMember.isAdmin()) {
return Mono.just(UserPermissionOnResourceStatus.success(buildAdminPermission(resourceType, resourceId, userId)));
}
return getAllMatchingPermissions0(userId, orgMember.getOrgId(), resourceType, Collections.singleton(resourceId), resourceAction)
.map(it -> it.getOrDefault(resourceId, emptyList()))
.map(permissions -> permissions.isEmpty() ? UserPermissionOnResourceStatus.notEnoughPermission()
: UserPermissionOnResourceStatus.success(getMaxPermission(permissions)));
})
.defaultIfEmpty(UserPermissionOnResourceStatus.notInOrg());

return Mono.zip(publicResourcePermissionMono, nonAnonymousPublicResourcePermissionMono, orgUserPermissionMono)
.map(tuple -> {
UserPermissionOnResourceStatus publicResourcePermission = tuple.getT1();
UserPermissionOnResourceStatus nonAnonymousPublicResourcePermission = tuple.getT2();
UserPermissionOnResourceStatus orgUserPermission = tuple.getT3();
if (orgUserPermission.hasPermission()) {
return orgUserPermission;
}
if(nonAnonymousPublicResourcePermission.hasPermission()) {
return nonAnonymousPublicResourcePermission;
}
if (publicResourcePermission.hasPermission()) {
return publicResourcePermission;
}
return orgUserPermission;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import javax.validation.constraints.NotNull;

import org.apache.commons.collections4.CollectionUtils;
import org.lowcoder.domain.application.model.ApplicationRequestType;
import org.lowcoder.domain.permission.model.ResourceAction;
import org.lowcoder.domain.permission.model.ResourceHolder;
import org.lowcoder.domain.permission.model.ResourcePermission;
Expand Down Expand Up @@ -221,6 +222,14 @@ public Mono<ResourcePermission> checkAndReturnMaxPermission(String userId, Strin
return resourcePermissionHandler.checkUserPermissionStatusOnResource(userId, resourceId, resourceAction);
}

public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnApplication
(String userId, String resourceId, ResourceAction resourceAction, ApplicationRequestType requestType) {
ResourceType resourceType = resourceAction.getResourceType();
var resourcePermissionHandler = getResourcePermissionHandler(resourceType);
return resourcePermissionHandler.checkUserPermissionStatusOnApplication(userId, resourceId, resourceAction, requestType);
}


public Mono<Boolean> removeUserApplicationPermission(String appId, String userId) {
return repository.removePermissionBy(ResourceType.APPLICATION, appId, ResourceHolder.USER, userId);
}
Expand Down
Loading