From 0554c856b25b9c92e1dae88b09a5844daec43146 Mon Sep 17 00:00:00 2001 From: sebastien moneuse Date: Mon, 9 Oct 2023 12:48:11 +0200 Subject: [PATCH] Support API V2 : Get / Create / Delete Permission targets --- .../jfrog/artifactory/client/Security.java | 11 ++ .../artifactory/client/model/Actions.java | 15 ++ .../client/model/PermissionTargetV2.java | 13 ++ .../client/model/PermissionV2.java | 15 ++ .../artifactory/client/model/PrivilegeV2.java | 27 +++ .../client/model/builder/ActionsBuilder.java | 13 ++ .../builder/PermissionTargetV2Builder.java | 16 ++ .../model/builder/PermissionV2Builder.java | 15 ++ .../model/builder/SecurityBuilders.java | 6 + .../client/impl/SecurityImpl.groovy | 44 +++++ .../client/model/impl/ActionsBuilderImpl.java | 35 ++++ .../client/model/impl/ActionsImpl.java | 76 +++++++++ .../impl/PermissionTargetV2BuilderImpl.java | 43 +++++ .../model/impl/PermissionTargetV2Impl.java | 80 +++++++++ .../model/impl/PermissionV2BuilderImpl.java | 58 +++++++ .../client/model/impl/PermissionV2Impl.java | 105 ++++++++++++ .../model/impl/SecurityBuildersImpl.java | 9 + .../client/ArtifactoryTestsBase.java | 38 +++++ .../artifactory/client/SecurityTests.java | 154 ++++++++++++++++++ 19 files changed, 773 insertions(+) create mode 100644 api/src/main/java/org/jfrog/artifactory/client/model/Actions.java create mode 100644 api/src/main/java/org/jfrog/artifactory/client/model/PermissionTargetV2.java create mode 100644 api/src/main/java/org/jfrog/artifactory/client/model/PermissionV2.java create mode 100644 api/src/main/java/org/jfrog/artifactory/client/model/PrivilegeV2.java create mode 100644 api/src/main/java/org/jfrog/artifactory/client/model/builder/ActionsBuilder.java create mode 100644 api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionTargetV2Builder.java create mode 100644 api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionV2Builder.java create mode 100644 services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsBuilderImpl.java create mode 100644 services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsImpl.java create mode 100644 services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2BuilderImpl.java create mode 100644 services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2Impl.java create mode 100644 services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2BuilderImpl.java create mode 100644 services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2Impl.java diff --git a/api/src/main/java/org/jfrog/artifactory/client/Security.java b/api/src/main/java/org/jfrog/artifactory/client/Security.java index 015b7111..4324adea 100644 --- a/api/src/main/java/org/jfrog/artifactory/client/Security.java +++ b/api/src/main/java/org/jfrog/artifactory/client/Security.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.jfrog.artifactory.client.model.Group; import org.jfrog.artifactory.client.model.PermissionTarget; +import org.jfrog.artifactory.client.model.PermissionTargetV2; import org.jfrog.artifactory.client.model.User; import org.jfrog.artifactory.client.model.builder.SecurityBuilders; @@ -30,6 +31,8 @@ public interface Security { PermissionTarget permissionTarget(String name); + PermissionTargetV2 permissionTargetV2(String name); + List permissionTargets(); void createOrUpdate(User user); @@ -38,17 +41,25 @@ public interface Security { void createOrReplacePermissionTarget(PermissionTarget permissionTarget); + void createOrReplacePermissionTargetV2(PermissionTargetV2 permissionTarget); + String deleteUser(String name); String deleteGroup(String name); String deletePermissionTarget(String name); + String deletePermissionTargetV2(String name); + String getSecurityApi(); + String getSecurityV2Api(); + String getSecurityUsersApi(); String getSecurityPermissionsApi(); + String getSecurityPermissionsV2Api(); + String getSecurityUserGroupsApi(); } diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/Actions.java b/api/src/main/java/org/jfrog/artifactory/client/model/Actions.java new file mode 100644 index 00000000..f2420385 --- /dev/null +++ b/api/src/main/java/org/jfrog/artifactory/client/model/Actions.java @@ -0,0 +1,15 @@ +package org.jfrog.artifactory.client.model; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface Actions { + Map> getUsers(); + Map> getGroups(); + + boolean isUserAllowedTo(String user, PrivilegeV2 privilege); + boolean isGroupAllowedTo(String group, PrivilegeV2 privilege); + @Override + boolean equals(Object o); +} diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/PermissionTargetV2.java b/api/src/main/java/org/jfrog/artifactory/client/model/PermissionTargetV2.java new file mode 100644 index 00000000..d3ca89d8 --- /dev/null +++ b/api/src/main/java/org/jfrog/artifactory/client/model/PermissionTargetV2.java @@ -0,0 +1,13 @@ +package org.jfrog.artifactory.client.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public interface PermissionTargetV2 { + String getName(); + PermissionV2 getRepo(); + PermissionV2 getBuild(); + PermissionV2 getReleaseBundle(); + @Override + boolean equals(Object o); +} diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/PermissionV2.java b/api/src/main/java/org/jfrog/artifactory/client/model/PermissionV2.java new file mode 100644 index 00000000..953ebead --- /dev/null +++ b/api/src/main/java/org/jfrog/artifactory/client/model/PermissionV2.java @@ -0,0 +1,15 @@ +package org.jfrog.artifactory.client.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.List; +import java.util.Map; +@JsonIgnoreProperties(ignoreUnknown = true) +public interface PermissionV2 { + List getIncludePatterns(); + List getExcludePatterns(); + List getRepositories(); + Actions getActions(); + @Override + boolean equals(Object o); +} diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/PrivilegeV2.java b/api/src/main/java/org/jfrog/artifactory/client/model/PrivilegeV2.java new file mode 100644 index 00000000..7efe64ab --- /dev/null +++ b/api/src/main/java/org/jfrog/artifactory/client/model/PrivilegeV2.java @@ -0,0 +1,27 @@ +package org.jfrog.artifactory.client.model; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum PrivilegeV2 { + READ("read"),WRITE("write"),ANNOTATE("annotate"),DELETE("delete"),MANAGE("manage"),MANAGED_XRAY_META("managedXrayMeta"),DISTRIBUTE("distribute"); + + @JsonValue + private String privilege; + + PrivilegeV2(String privilege) { + this.privilege = privilege; + } + + public String getPrivilege() { + return privilege; + } + + public static PrivilegeV2 fromPrivilege(String privilege){ + for (PrivilegeV2 privilegeV2 : values()) { + if (privilegeV2.privilege.equals(privilege)) { + return privilegeV2; + } + } + throw new IllegalArgumentException("No Privilege for "+privilege+" found."); + } +} diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/builder/ActionsBuilder.java b/api/src/main/java/org/jfrog/artifactory/client/model/builder/ActionsBuilder.java new file mode 100644 index 00000000..d80a08f8 --- /dev/null +++ b/api/src/main/java/org/jfrog/artifactory/client/model/builder/ActionsBuilder.java @@ -0,0 +1,13 @@ +package org.jfrog.artifactory.client.model.builder; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.jfrog.artifactory.client.model.Actions; +import org.jfrog.artifactory.client.model.PrivilegeV2; + +@JsonIgnoreProperties(ignoreUnknown = true) +public interface ActionsBuilder { + + ActionsBuilder addUser(String user, PrivilegeV2... privileges); + ActionsBuilder addGroup(String group, PrivilegeV2... privileges); + Actions build(); +} diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionTargetV2Builder.java b/api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionTargetV2Builder.java new file mode 100644 index 00000000..3b154264 --- /dev/null +++ b/api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionTargetV2Builder.java @@ -0,0 +1,16 @@ +package org.jfrog.artifactory.client.model.builder; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.jfrog.artifactory.client.model.PermissionTargetV2; +import org.jfrog.artifactory.client.model.PermissionV2; + +@JsonIgnoreProperties(ignoreUnknown = true) +public interface PermissionTargetV2Builder { + + PermissionTargetV2Builder name(String name); + PermissionTargetV2Builder repo(PermissionV2 repo); + PermissionTargetV2Builder build(PermissionV2 build); + PermissionTargetV2Builder releaseBundle(PermissionV2 releaseBundle); + PermissionTargetV2 build(); + +} diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionV2Builder.java b/api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionV2Builder.java new file mode 100644 index 00000000..95f42fac --- /dev/null +++ b/api/src/main/java/org/jfrog/artifactory/client/model/builder/PermissionV2Builder.java @@ -0,0 +1,15 @@ +package org.jfrog.artifactory.client.model.builder; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.jfrog.artifactory.client.model.Actions; +import org.jfrog.artifactory.client.model.PermissionV2; + +@JsonIgnoreProperties(ignoreUnknown = true) +public interface PermissionV2Builder { + + PermissionV2Builder includePatterns(String... includePatterns); + PermissionV2Builder excludePatterns(String... excludePatterns); + PermissionV2Builder repositories(String... repositories); + PermissionV2Builder actions(Actions actions); + PermissionV2 build(); +} diff --git a/api/src/main/java/org/jfrog/artifactory/client/model/builder/SecurityBuilders.java b/api/src/main/java/org/jfrog/artifactory/client/model/builder/SecurityBuilders.java index f302fac2..69299bbd 100644 --- a/api/src/main/java/org/jfrog/artifactory/client/model/builder/SecurityBuilders.java +++ b/api/src/main/java/org/jfrog/artifactory/client/model/builder/SecurityBuilders.java @@ -25,4 +25,10 @@ public interface SecurityBuilders { PrincipalsBuilder principalsBuilder(); PrincipalBuilder principalBuilder(); + + PermissionTargetV2Builder permissionTargetV2Builder(); + + PermissionV2Builder permissionV2Builder(); + + ActionsBuilder actionsBuilder(); } diff --git a/services/src/main/groovy/org/jfrog/artifactory/client/impl/SecurityImpl.groovy b/services/src/main/groovy/org/jfrog/artifactory/client/impl/SecurityImpl.groovy index 2dcc1ba3..c0ef1eb2 100644 --- a/services/src/main/groovy/org/jfrog/artifactory/client/impl/SecurityImpl.groovy +++ b/services/src/main/groovy/org/jfrog/artifactory/client/impl/SecurityImpl.groovy @@ -1,12 +1,15 @@ package org.jfrog.artifactory.client.impl +import org.apache.commons.lang3.StringUtils import org.apache.http.entity.ContentType import org.jfrog.artifactory.client.Security import org.jfrog.artifactory.client.impl.util.Util import org.jfrog.artifactory.client.model.Group import org.jfrog.artifactory.client.model.PermissionTarget +import org.jfrog.artifactory.client.model.PermissionTargetV2 import org.jfrog.artifactory.client.model.User import org.jfrog.artifactory.client.model.builder.SecurityBuilders +import org.jfrog.artifactory.client.model.impl.PermissionTargetV2Impl import org.jfrog.artifactory.client.model.impl.SecurityBuildersImpl import org.jfrog.artifactory.client.model.impl.UserBuilderImpl import org.jfrog.artifactory.client.model.impl.GroupImpl @@ -60,6 +63,12 @@ class SecurityImpl implements Security { return artifactory.get("${getSecurityPermissionsApi()}/$name", PermissionTargetImpl, PermissionTarget) } + @Override + PermissionTargetV2 permissionTargetV2(String name){ + name = Util.encodeParams(name); + return artifactory.get("${getSecurityPermissionsV2Api()}/$name", PermissionTargetV2Impl, PermissionTargetV2) + } + @Override List groupNames() { GroupImpl[] groups = artifactory.get("${getSecurityUserGroupsApi()}", GroupImpl[], null) @@ -99,6 +108,25 @@ class SecurityImpl implements Security { new HashMap(), null, -1, String, null) } + @Override + void createOrReplacePermissionTargetV2(PermissionTargetV2 permissionTarget) { + if(permissionTarget == null || StringUtils.isBlank(permissionTarget.getName())){ + throw new IllegalArgumentException("Permission target name is required") + } + if (permissionTarget.getRepo() == null || permissionTarget.getRepo().getRepositories()==null || permissionTarget.getRepo().getRepositories().isEmpty()) { + throw new UnsupportedOperationException("At least 1 repository is required in permission target (could be 'ANY', 'ANY LOCAL', 'ANY REMOTE')") + } + if(permissionTarget.getBuild() != null && permissionTarget.getBuild().getRepositories() !=null){ + List buildRepositories = permissionTarget.getBuild().getRepositories(); + if(buildRepositories.size() !=1 || !buildRepositories.contains("artifactory-build-info")){ + throw new UnsupportedOperationException("Only 'artifactory-build-info' repository is supported for build permission target") + } + } + String name = Util.encodeParams(permissionTarget.getName()); + artifactory.put("${getSecurityPermissionsV2Api()}/$name", ContentType.APPLICATION_JSON, Util.getStringFromObject(permissionTarget), + new HashMap(), null, -1, String, null) + } + @Override String deleteUser(String name) { name = Util.encodeParams(name); @@ -117,11 +145,22 @@ class SecurityImpl implements Security { artifactory.delete("${getSecurityPermissionsApi()}/$name") } + @Override + String deletePermissionTargetV2(String name) { + name = Util.encodeParams(name); + artifactory.delete("${getSecurityPermissionsV2Api()}/$name") + } + @Override String getSecurityApi() { return baseApiPath + "/security/"; } + @Override + String getSecurityV2Api() { + return baseApiPath + "/v2/security/"; + } + @Override String getSecurityUsersApi() { return getSecurityApi() + "users"; @@ -132,6 +171,11 @@ class SecurityImpl implements Security { return getSecurityApi() + "permissions"; } + @Override + String getSecurityPermissionsV2Api() { + return getSecurityV2Api() + "permissions"; + } + @Override String getSecurityUserGroupsApi() { return getSecurityApi() + "groups"; diff --git a/services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsBuilderImpl.java b/services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsBuilderImpl.java new file mode 100644 index 00000000..135305ca --- /dev/null +++ b/services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsBuilderImpl.java @@ -0,0 +1,35 @@ +package org.jfrog.artifactory.client.model.impl; + +import org.jfrog.artifactory.client.model.Actions; +import org.jfrog.artifactory.client.model.PrivilegeV2; +import org.jfrog.artifactory.client.model.builder.ActionsBuilder; + +import java.util.*; + +public class ActionsBuilderImpl implements ActionsBuilder { + private Map> usersGrantedActions; + private Map> groupsGrantedActions; + + public ActionsBuilderImpl() { + this.usersGrantedActions= new HashMap<>(); + this.groupsGrantedActions = new HashMap<>(); + } + @Override + public ActionsBuilder addUser(String user, PrivilegeV2... privileges) { + Set userPrivileges = new HashSet<>(Arrays.asList(privileges)); + usersGrantedActions.put(user, userPrivileges); + return this; + } + + @Override + public ActionsBuilder addGroup(String group, PrivilegeV2... privileges) { + Set groupPrivileges = new HashSet<>(Arrays.asList(privileges)); + groupsGrantedActions.put(group, groupPrivileges); + return this; + } + + @Override + public Actions build() { + return new ActionsImpl(usersGrantedActions, groupsGrantedActions); + } +} diff --git a/services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsImpl.java b/services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsImpl.java new file mode 100644 index 00000000..b1dfc59d --- /dev/null +++ b/services/src/main/java/org/jfrog/artifactory/client/model/impl/ActionsImpl.java @@ -0,0 +1,76 @@ +package org.jfrog.artifactory.client.model.impl; + +import org.apache.commons.lang3.StringUtils; +import org.jfrog.artifactory.client.model.Actions; +import org.jfrog.artifactory.client.model.PrivilegeV2; + +import java.util.*; + +public class ActionsImpl implements Actions { + + private Map> users; + + private Map> groups; + + public ActionsImpl() { + super(); + this.users=new HashMap<>(); + this.groups=new HashMap<>(); + } + + public ActionsImpl(Map> users, Map> groups) { + this.users = Optional.ofNullable(users).orElse(Collections.emptyMap()); + this.groups = Optional.ofNullable(groups).orElse(Collections.emptyMap()); + } + + @Override + public boolean isUserAllowedTo(String user, PrivilegeV2 privilege) { + if(StringUtils.isBlank(user) || privilege == null) { + return false; + } + return users.containsKey(user) && users.get(user).contains(privilege); + } + + @Override + public boolean isGroupAllowedTo(String group, PrivilegeV2 privilege) { + if(StringUtils.isBlank(group) || privilege == null) { + return false; + } + return groups.containsKey(group) && groups.get(group).contains(privilege); + } + + @Override + public Map> getUsers() { + return users; + } + + @Override + public Map> getGroups() { + return groups; + } + + public void setUsers(Map> users) { + this.users = users; + } + + public void setGroups(Map> groups) { + this.groups = groups; + } + + @Override + public boolean equals(Object obj) { + if(!(obj instanceof Actions)) { + return false; + } + Actions other = (Actions) obj; + boolean areEquals = (users==null && other.getUsers()==null) || + (users==null && other.getUsers().isEmpty()) || + (users.isEmpty() && other.getUsers()==null) || + (users!=null && users.equals(other.getUsers())); + areEquals &= (groups==null && other.getGroups()==null) || + (groups==null && other.getGroups().isEmpty()) || + (groups.isEmpty() && other.getGroups()==null) || + (groups!=null && groups.equals(other.getGroups())); + return areEquals; + } +} diff --git a/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2BuilderImpl.java b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2BuilderImpl.java new file mode 100644 index 00000000..542a137b --- /dev/null +++ b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2BuilderImpl.java @@ -0,0 +1,43 @@ +package org.jfrog.artifactory.client.model.impl; + +import org.jfrog.artifactory.client.model.PermissionTargetV2; +import org.jfrog.artifactory.client.model.PermissionV2; +import org.jfrog.artifactory.client.model.builder.PermissionTargetV2Builder; + +public class PermissionTargetV2BuilderImpl implements PermissionTargetV2Builder { + private String name; + private PermissionV2 repo; + private PermissionV2 build; + private PermissionV2 releaseBundle; + @Override + public PermissionTargetV2Builder name(String name) { + this.name = name; + return this; + } + + @Override + public PermissionTargetV2Builder repo(PermissionV2 repo) { + this.repo = repo; + return this; + } + + @Override + public PermissionTargetV2Builder build(PermissionV2 build) { + this.build = build; + return this; + } + + @Override + public PermissionTargetV2Builder releaseBundle(PermissionV2 releaseBundle) { + this.releaseBundle = releaseBundle; + return this; + } + + @Override + public PermissionTargetV2 build() { + return new PermissionTargetV2Impl(this.name, + this.repo, + this.build, + this.releaseBundle); + } +} diff --git a/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2Impl.java b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2Impl.java new file mode 100644 index 00000000..87330435 --- /dev/null +++ b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionTargetV2Impl.java @@ -0,0 +1,80 @@ +package org.jfrog.artifactory.client.model.impl; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.apache.commons.lang3.StringUtils; +import org.jfrog.artifactory.client.model.PermissionTargetV2; +import org.jfrog.artifactory.client.model.PermissionV2; + +import java.util.Optional; + +public class PermissionTargetV2Impl implements PermissionTargetV2 { + private String name; + @JsonProperty("repo") + @JsonDeserialize(as = PermissionV2Impl.class) + private PermissionV2 repo; + @JsonProperty("build") + @JsonDeserialize(as = PermissionV2Impl.class) + private PermissionV2 build; + @JsonProperty("releaseBundle") + @JsonDeserialize(as = PermissionV2Impl.class) + private PermissionV2 releaseBundle; + + public PermissionTargetV2Impl() { + this.repo=new PermissionV2Impl(); + this.build= new PermissionV2Impl(); + this.releaseBundle= new PermissionV2Impl(); + } + + public PermissionTargetV2Impl(String name, PermissionV2 repo, PermissionV2 build, PermissionV2 releaseBundle) { + this.name = Optional.ofNullable(name).orElse(StringUtils.EMPTY); + this.repo = Optional.ofNullable(repo).orElse(new PermissionV2Impl()); + this.build = Optional.ofNullable(build).orElse(new PermissionV2Impl()); + this.releaseBundle = Optional.ofNullable(releaseBundle).orElse(new PermissionV2Impl()); + } + @Override + public String getName() { + return name; + } + @Override + public PermissionV2 getRepo() { + return repo; + } + @Override + public PermissionV2 getBuild() { + return build; + } + @Override + public PermissionV2 getReleaseBundle() { + return releaseBundle; + } + + public void setName(String name) { + this.name = name; + } + + public void setRepo(PermissionV2 repo) { + this.repo = repo; + } + + public void setBuild(PermissionV2 build) { + this.build = build; + } + + public void setReleaseBundle(PermissionV2 releaseBundle) { + this.releaseBundle = releaseBundle; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PermissionTargetV2)) { + return false; + } + PermissionTargetV2 other = (PermissionTargetV2) obj; + boolean areEquals = StringUtils.equals(this.name, other.getName()); + areEquals &= (this.repo == null && other.getRepo() == null) || (this.repo != null && this.repo.equals(other.getRepo())); + areEquals &= (this.build == null && other.getBuild() == null) || (this.build != null && this.build.equals(other.getBuild())); + areEquals &= (this.releaseBundle == null && other.getReleaseBundle() == null) || (this.releaseBundle != null && this.releaseBundle.equals(other.getReleaseBundle())); + return areEquals; + } +} diff --git a/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2BuilderImpl.java b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2BuilderImpl.java new file mode 100644 index 00000000..97d132cd --- /dev/null +++ b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2BuilderImpl.java @@ -0,0 +1,58 @@ +package org.jfrog.artifactory.client.model.impl; + +import org.jfrog.artifactory.client.model.Actions; +import org.jfrog.artifactory.client.model.PermissionV2; +import org.jfrog.artifactory.client.model.builder.PermissionV2Builder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PermissionV2BuilderImpl implements PermissionV2Builder { + + + private List includePatterns; + private List excludePatterns; + private List repositories; + private Actions actions; + + public PermissionV2BuilderImpl() { + this.includePatterns = new ArrayList<>(); + this.excludePatterns = new ArrayList<>(); + this.repositories = new ArrayList<>(); + } + + @Override + public PermissionV2Builder includePatterns(String... includePatterns) { + this.includePatterns= Arrays.asList(includePatterns); + return this; + } + + @Override + public PermissionV2Builder excludePatterns(String... excludePatterns) { + this.excludePatterns= Arrays.asList(excludePatterns); + return this; + } + + @Override + public PermissionV2Builder repositories(String... repositories) { + this.repositories= Arrays.asList(repositories); + return this; + } + + @Override + public PermissionV2Builder actions(Actions actions) { + this.actions=actions; + return this; + } + + @Override + public PermissionV2 build() { + PermissionV2Impl permissionV2 = new PermissionV2Impl(); + permissionV2.setActions(this.actions); + permissionV2.setRepositories(this.repositories); + permissionV2.setIncludePatterns(this.includePatterns); + permissionV2.setExcludePatterns(this.excludePatterns); + return permissionV2; + } +} diff --git a/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2Impl.java b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2Impl.java new file mode 100644 index 00000000..cbc43d1d --- /dev/null +++ b/services/src/main/java/org/jfrog/artifactory/client/model/impl/PermissionV2Impl.java @@ -0,0 +1,105 @@ +package org.jfrog.artifactory.client.model.impl; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.jfrog.artifactory.client.model.Actions; +import org.jfrog.artifactory.client.model.PermissionV2; +import org.jfrog.artifactory.client.model.PrivilegeV2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class PermissionV2Impl implements PermissionV2 { + @JsonProperty("include-patterns") + private List includePatterns; + @JsonProperty("exclude-patterns") + private List excludePatterns; + private List repositories; + @JsonDeserialize(as = ActionsImpl.class) + private Actions actions; + + //Required for JSON parsing of PermissionV2Impl + public PermissionV2Impl() { + this.includePatterns= new ArrayList<>(); + this.excludePatterns= new ArrayList<>(); + this.repositories= new ArrayList<>(); + this.actions= new ActionsImpl(); + } + + public PermissionV2Impl(List includePatterns, List excludePatterns, List repositories, Actions actions) { + this.includePatterns = Optional.ofNullable(includePatterns).orElse(Collections.emptyList()); + this.excludePatterns = Optional.ofNullable(excludePatterns).orElse(Collections.emptyList()); + this.repositories = Optional.ofNullable(repositories).orElse(Collections.emptyList()); + this.actions = Optional.ofNullable(actions).orElse(new ActionsImpl()); + } + + public boolean isUserAllowedTo(String user, PrivilegeV2 privilege){ + return actions.isUserAllowedTo(user, privilege); + } + + public boolean isGroupAllowedTo(String group, PrivilegeV2 privilege){ + return actions.isGroupAllowedTo(group, privilege); + } + + @Override + public List getIncludePatterns() { + return includePatterns; + } + + @Override + public List getExcludePatterns() { + return excludePatterns; + } + + @Override + public List getRepositories() { + return repositories; + } + @Override + public Actions getActions() { + return actions; + } + + public void setActions(Actions actions) { + this.actions = actions; + } + + public void setRepositories(List repositories) { + this.repositories = repositories; + } + + public void setIncludePatterns(List includePatterns) { + this.includePatterns = includePatterns; + } + + public void setExcludePatterns(List excludePatterns) { + this.excludePatterns = excludePatterns; + } + + @Override + public boolean equals(Object obj) { + if(!(obj instanceof PermissionV2)) { + return false; + } + PermissionV2 other = (PermissionV2) obj; + boolean areEquals = (includePatterns==null && other.getIncludePatterns()==null) || + (includePatterns==null && other.getIncludePatterns().isEmpty()) || + (includePatterns.isEmpty() && other.getIncludePatterns()==null) || + (includePatterns!=null && includePatterns.equals(other.getIncludePatterns())); + + areEquals &= (excludePatterns==null && other.getExcludePatterns()==null) || + (excludePatterns==null && other.getExcludePatterns().isEmpty()) || + (excludePatterns.isEmpty() && other.getExcludePatterns()==null) || + (excludePatterns!=null && excludePatterns.equals(other.getExcludePatterns())); + + areEquals &= (repositories==null && other.getRepositories()==null) || + (repositories==null && other.getRepositories().isEmpty()) || + (repositories.isEmpty() && other.getRepositories()==null) || + (repositories!=null && repositories.equals(other.getRepositories())); + + areEquals &= (actions==null && other.getActions()==null) || (actions!=null && actions.equals(other.getActions())); + return areEquals; + } +} diff --git a/services/src/main/java/org/jfrog/artifactory/client/model/impl/SecurityBuildersImpl.java b/services/src/main/java/org/jfrog/artifactory/client/model/impl/SecurityBuildersImpl.java index 3fd9f8f5..fef63477 100644 --- a/services/src/main/java/org/jfrog/artifactory/client/model/impl/SecurityBuildersImpl.java +++ b/services/src/main/java/org/jfrog/artifactory/client/model/impl/SecurityBuildersImpl.java @@ -45,4 +45,13 @@ public PrincipalsBuilder principalsBuilder() { public PrincipalBuilder principalBuilder() { return new PrincipalBuilderImpl(); } + + @Override + public PermissionTargetV2Builder permissionTargetV2Builder() { return new PermissionTargetV2BuilderImpl(); } + + @Override + public PermissionV2Builder permissionV2Builder() { return new PermissionV2BuilderImpl(); } + + @Override + public ActionsBuilder actionsBuilder() { return new ActionsBuilderImpl(); } } diff --git a/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java b/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java index 13b254a8..b817bb4c 100644 --- a/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java +++ b/services/src/test/java/org/jfrog/artifactory/client/ArtifactoryTestsBase.java @@ -191,6 +191,44 @@ protected String deleteRepoIfExists(String repoName) { } } } + protected void deletePermissionTargetV2IfExists(String name){ + if (StringUtils.isBlank(name)) { return ; } + try { + artifactory.security().deletePermissionTargetV2(name); + } catch (Exception e) { + if (e instanceof HttpResponseException && ((HttpResponseException)e).getStatusCode() == 404) { + return; + } else { + throw e; + } + } + } + + protected void deleteUserIfExists(String userName){ + if (StringUtils.isBlank(userName)) { return ; } + try { + artifactory.security().deleteUser(userName); + } catch (Exception e) { + if (e instanceof HttpResponseException && ((HttpResponseException)e).getStatusCode() == 404) { + return; + } else { + throw e; + } + } + } + + protected void deleteGroupIfExists(String groupName){ + if (StringUtils.isBlank(groupName)) { return ; } + try { + artifactory.security().deleteGroup(groupName); + } catch (Exception e) { + if (e instanceof HttpResponseException && ((HttpResponseException)e).getStatusCode() == 404) { + return; + } else { + throw e; + } + } + } public static String calcSha1(InputStream content) { MessageDigest md = null; diff --git a/services/src/test/java/org/jfrog/artifactory/client/SecurityTests.java b/services/src/test/java/org/jfrog/artifactory/client/SecurityTests.java index c4f15b42..e51ff330 100644 --- a/services/src/test/java/org/jfrog/artifactory/client/SecurityTests.java +++ b/services/src/test/java/org/jfrog/artifactory/client/SecurityTests.java @@ -193,6 +193,160 @@ private PermissionTarget getAndAssertPermissionTarget(String expectedRepoName) t throw permissionTargetException; } + @Test + public void testCreatePermissionTargetV2(){ + final String USER_TEST_1="user_test_1_"+System.currentTimeMillis(); + final String USER_TEST_2="user_test_2_"+System.currentTimeMillis(); + final String GROUP_TEST_1="group_test_1_"+System.currentTimeMillis(); + final String GROUP_TEST_2="group_test_2_"+System.currentTimeMillis(); + final String PERMISSION_TARGET_NAME="permission_target_v2_"+System.currentTimeMillis(); + try{ + //Eventual clean up of previous test run + deletePermissionTargetV2IfExists(PERMISSION_TARGET_NAME); + deleteUserIfExists(USER_TEST_1); + deleteUserIfExists(USER_TEST_2); + deleteGroupIfExists(GROUP_TEST_1); + deleteGroupIfExists(GROUP_TEST_2); + //Test environment setUp + User user1 = artifactory.security().builders().userBuilder().name(USER_TEST_1).email("user1@test.com") + .admin(false).profileUpdatable(true).password("YouShallNotPass123!").build(); + User user2 = artifactory.security().builders().userBuilder().name(USER_TEST_2).email("user2@test.com") + .admin(false).profileUpdatable(true).password("YouShallNotPass123!").build(); + Group group1 = artifactory.security().builders().groupBuilder().name(GROUP_TEST_1).autoJoin(true) + .description("test-group").build(); + Group group2 = artifactory.security().builders().groupBuilder().name(GROUP_TEST_2).autoJoin(true) + .description("test-group").build(); + artifactory.security().createOrUpdate(user1); + artifactory.security().createOrUpdate(user2); + artifactory.security().createOrUpdateGroup(group1); + artifactory.security().createOrUpdateGroup(group2); + + Actions repositoryActions = artifactory.security().builders().actionsBuilder() + .addUser(user1.getName(), PrivilegeV2.READ, PrivilegeV2.ANNOTATE) + .addGroup(group1.getName(), PrivilegeV2.DELETE, PrivilegeV2.MANAGE) + .build(); + Actions buildActions = artifactory.security().builders().actionsBuilder() + .addUser(user2.getName(), PrivilegeV2.DISTRIBUTE, PrivilegeV2.WRITE, PrivilegeV2.READ) + .addGroup(group2.getName(), PrivilegeV2.MANAGE, PrivilegeV2.WRITE) + .build(); + Actions releaseBundleActions = artifactory.security().builders().actionsBuilder() + .addUser(user1.getName(), PrivilegeV2.READ) + .addUser(user2.getName(), PrivilegeV2.WRITE, PrivilegeV2.READ, PrivilegeV2.DELETE) + .build(); + + PermissionV2 repositoryPermission = artifactory.security().builders().permissionV2Builder() + .includePatterns("aaa*/**", "bbb*/**") + .excludePatterns("ccc*/**", "ddd*/**") + .repositories("ANY LOCAL") + .actions(repositoryActions) + .build(); + PermissionV2 buildPermission = artifactory.security().builders().permissionV2Builder() + .includePatterns("eee*/**", "fff*/**") + .excludePatterns("ggg*/**", "hhh*/**") + .repositories("artifactory-build-info") + .actions(buildActions) + .build(); + PermissionV2 releaseBundlePermission = artifactory.security().builders().permissionV2Builder() + .includePatterns("iii*/**", "jjj*/**") + .excludePatterns("kkk*/**","lll*/**") + .repositories("ANY") + .actions(releaseBundleActions) + .build(); + + PermissionTargetV2 permissionTargetV2 = artifactory.security().builders().permissionTargetV2Builder() + .name(PERMISSION_TARGET_NAME) + .repo(repositoryPermission) + .build(buildPermission) + .releaseBundle(releaseBundlePermission) + .build(); + artifactory.security().createOrReplacePermissionTargetV2(permissionTargetV2); + + //Now fetch the just created permission target + PermissionTargetV2 storedPermissionTarget = artifactory.security().permissionTargetV2(PERMISSION_TARGET_NAME); + assertNotNull(storedPermissionTarget,"Got null object as PermissionTargetV2 after create"); + assertTrue(permissionTargetV2.equals(storedPermissionTarget), "Prepared permission target and created one are not equals"); + } + finally { + //Remove test items + deletePermissionTargetV2IfExists(PERMISSION_TARGET_NAME); + deleteUserIfExists(USER_TEST_1); + deleteUserIfExists(USER_TEST_2); + deleteGroupIfExists(GROUP_TEST_1); + deleteGroupIfExists(GROUP_TEST_2); + } + } + + @Test(expectedExceptions = {UnsupportedOperationException.class}, expectedExceptionsMessageRegExp = "Only 'artifactory-build-info' repository .*") + public void testCreatePermissionV2WithBuildError(){ + final String GROUP_TEST="group_test_"+System.currentTimeMillis(); + final String PERMISSION_TARGET_NAME="permission_target_v2_"+System.currentTimeMillis(); + try{ + // remove eventual previous test run leftovers + deletePermissionTargetV2IfExists(PERMISSION_TARGET_NAME); + deleteGroupIfExists(GROUP_TEST); + + // create test environment + Group group= artifactory.security().builders().groupBuilder().name(GROUP_TEST).autoJoin(true) + .description("test-group").build(); + artifactory.security().createOrUpdateGroup(group); + + Actions buildActions = artifactory.security().builders().actionsBuilder() + .addGroup(group.getName(), PrivilegeV2.MANAGE, PrivilegeV2.WRITE) + .build(); + PermissionV2 buildPermission = artifactory.security().builders().permissionV2Builder() + .includePatterns("aaa*/**", "bbb*/**") + .excludePatterns("ccc*/**", "ddd*/**") + .repositories("should-throw-error") + .actions(buildActions) + .build(); + PermissionV2 repositoryPermission = artifactory.security().builders().permissionV2Builder() + .repositories("ANY") + .build(); + PermissionTargetV2 permissionTargetV2 = artifactory.security().builders().permissionTargetV2Builder() + .name(PERMISSION_TARGET_NAME) + .repo(repositoryPermission) + .build(buildPermission) + .build(); + artifactory.security().createOrReplacePermissionTargetV2(permissionTargetV2); + fail("Should have thrown exception"); + } + finally { + //clean up test items + deletePermissionTargetV2IfExists(PERMISSION_TARGET_NAME); + deleteGroupIfExists(GROUP_TEST); + } + } + + @Test(expectedExceptions = {UnsupportedOperationException.class}, expectedExceptionsMessageRegExp = "At least 1 repository is required .*") + public void testCreatePermissionV2MissingRepoPermissionError(){ + final String GROUP_TEST="group_test_"+System.currentTimeMillis(); + final String PERMISSION_TARGET_NAME="permission_target_v2_"+System.currentTimeMillis(); + try{ + // remove eventual previous test run leftovers + deletePermissionTargetV2IfExists(PERMISSION_TARGET_NAME); + deleteGroupIfExists(GROUP_TEST); + + // create test environment + Group group= artifactory.security().builders().groupBuilder().name(GROUP_TEST).autoJoin(true) + .description("test-group").build(); + artifactory.security().createOrUpdateGroup(group); + + PermissionV2 repositoryPermission = artifactory.security().builders().permissionV2Builder() + .build(); + PermissionTargetV2 permissionTargetV2 = artifactory.security().builders().permissionTargetV2Builder() + .name(PERMISSION_TARGET_NAME) + .repo(repositoryPermission) + .build(); + artifactory.security().createOrReplacePermissionTargetV2(permissionTargetV2); + fail("Should have thrown exception"); + } + finally { + //clean up test items + deletePermissionTargetV2IfExists(PERMISSION_TARGET_NAME); + deleteGroupIfExists(GROUP_TEST); + } + } + @Test public void testCreateGroup() { GroupBuilder groupBuilder = artifactory.security().builders().groupBuilder();