diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..12301490
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
new file mode 100644
index 00000000..0d0b1c99
--- /dev/null
+++ b/.github/release-drafter.yml
@@ -0,0 +1 @@
+_extends: .github
diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml
new file mode 100644
index 00000000..ff6a66d5
--- /dev/null
+++ b/.github/workflows/cd.yaml
@@ -0,0 +1,58 @@
+# Note: additional setup is required, see https://github.com/jenkinsci/incrementals-tools#automatic-deployment
+
+name: cd
+on:
+ workflow_dispatch:
+ check_run:
+ types:
+ - completed
+
+jobs:
+ validate:
+ runs-on: ubuntu-latest
+ outputs:
+ should_release: ${{ steps.verify-ci-status.outputs.result == 'success' && steps.interesting-categories.outputs.interesting == 'true' }}
+ steps:
+ - name: Verify CI status
+ uses: jenkins-infra/verify-ci-status-action@v1.2.0
+ id: verify-ci-status
+ with:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ output_result: true
+
+ - name: Release Drafter
+ uses: release-drafter/release-drafter@v5
+ if: steps.verify-ci-status.outputs.result == 'success'
+ with:
+ name: next
+ tag: next
+ version: next
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Check interesting categories
+ uses: jenkins-infra/interesting-category-action@v1.0.0
+ id: interesting-categories
+ if: steps.verify-ci-status.outputs.result == 'success'
+ with:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ release:
+ runs-on: ubuntu-latest
+ needs: [validate]
+ if: needs.validate.outputs.should_release == 'true'
+ steps:
+ - name: Check out
+ uses: actions/checkout@v2.3.4
+ with:
+ fetch-depth: 0
+ - name: Set up JDK 8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Release
+ uses: jenkins-infra/jenkins-maven-cd-action@v1.1.0
+ with:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
+ MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }}
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
index 112bdd40..43d62816 100644
--- a/.mvn/extensions.xml
+++ b/.mvn/extensions.xml
@@ -1,7 +1,7 @@
-
- io.jenkins.tools.incrementals
- git-changelist-maven-extension
- 1.0-beta-7
-
-
\ No newline at end of file
+
+ io.jenkins.tools.incrementals
+ git-changelist-maven-extension
+ 1.2
+
+
diff --git a/.mvn/maven.config b/.mvn/maven.config
index ac318abc..f7daf60d 100644
--- a/.mvn/maven.config
+++ b/.mvn/maven.config
@@ -1,2 +1,3 @@
-Pconsume-incrementals
--Pmight-produce-incrementals
\ No newline at end of file
+-Pmight-produce-incrementals
+-Dchangelist.format=%d.v%s
diff --git a/CHANGELOG.md b/CHANGELOG-old.md
similarity index 93%
rename from CHANGELOG.md
rename to CHANGELOG-old.md
index be33865a..a959daa6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG-old.md
@@ -1,5 +1,7 @@
# Changelog
+See [GitHub releases](https://github.com/jenkinsci/azure-ad-plugin/releases) for all newer versions.
+
## 1.2.3 (2021-02-20)
* Document update
diff --git a/checkstyle.xml b/checkstyle.xml
index 37af10a6..2328ddc3 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -4,8 +4,8 @@
-->
+ "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
+ "https://checkstyle.org/dtds/configuration_1_3.dtd">
@@ -76,9 +80,6 @@
-
-
-
@@ -136,11 +137,10 @@
-
+
-
-
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 374dc943..6a13b308 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,11 +5,11 @@
org.jenkins-ci.plugins
plugin
- 4.15
+ 4.17
azure-ad
- ${revision}${changelist}
+ ${changelist}
Azure AD Plugin
A Jenkins authentication & authorization plugin for Azure Active Directory
@@ -37,15 +37,15 @@
- scm:git:ssh://github.com/jenkinsci/azure-ad-plugin.git
- scm:git:ssh://git@github.com/jenkinsci/azure-ad-plugin.git
- https://github.com/jenkinsci/azure-ad-plugin
+ scm:git:ssh://github.com/${gitHubRepo}.git
+ scm:git:ssh://git@github.com/${gitHubRepo}.git
+ https://github.com/${gitHubRepo}
${scmTag}
- 1.2.4
- -SNAPSHOT
+ 9999-SNAPSHOT
+ jenkinsci/azure-ad-plugin
UTF-8
UTF-8
@@ -180,7 +180,7 @@
io.jenkins.tools.bom
bom-2.263.x
- 19
+ 26
import
pom
@@ -203,7 +203,7 @@
com.google.errorprone
error_prone_annotations
- 2.3.4
+ 2.4.0
org.ow2.asm
@@ -289,7 +289,14 @@
org.apache.maven.plugins
maven-checkstyle-plugin
- 2.17
+ 3.1.1
+
+
+ com.puppycrawl.tools
+ checkstyle
+ 8.41.1
+
+
validate
diff --git a/src/main/java/com/microsoft/jenkins/azuread/AzureAdGroup.java b/src/main/java/com/microsoft/jenkins/azuread/AzureAdGroup.java
index daf28f5e..eecf8d5d 100644
--- a/src/main/java/com/microsoft/jenkins/azuread/AzureAdGroup.java
+++ b/src/main/java/com/microsoft/jenkins/azuread/AzureAdGroup.java
@@ -1,41 +1,41 @@
-/*
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See LICENSE file in the project root for license information.
- */
-
-package com.microsoft.jenkins.azuread;
-
-import org.acegisecurity.GrantedAuthority;
-
-public class AzureAdGroup implements GrantedAuthority {
-
- private String objectId;
-
- private String groupName;
-
- public AzureAdGroup(String objectId, String groupName) {
- this.objectId = objectId;
- this.groupName = groupName;
- }
-
- @Override
- public String getAuthority() {
- return groupName;
- }
-
- public String getObjectId() {
- return objectId;
- }
-
- public String getGroupName() {
- return groupName;
- }
-
- @Override
- public String toString() {
- return "AzureAdGroup{"
- + "objectId='" + objectId + '\''
- + ", groupName='" + groupName + '\''
- + '}';
- }
-}
+/*
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE file in the project root for license information.
+ */
+
+package com.microsoft.jenkins.azuread;
+
+import org.acegisecurity.GrantedAuthority;
+
+public class AzureAdGroup implements GrantedAuthority {
+
+ private String objectId;
+
+ private String groupName;
+
+ public AzureAdGroup(String objectId, String groupName) {
+ this.objectId = objectId;
+ this.groupName = groupName;
+ }
+
+ @Override
+ public String getAuthority() {
+ return groupName;
+ }
+
+ public String getObjectId() {
+ return objectId;
+ }
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ @Override
+ public String toString() {
+ return "AzureAdGroup{"
+ + "objectId='" + objectId + '\''
+ + ", groupName='" + groupName + '\''
+ + '}';
+ }
+}
diff --git a/src/main/java/com/microsoft/jenkins/azuread/AzureAdMatrixAuthorizationStrategy.java b/src/main/java/com/microsoft/jenkins/azuread/AzureAdMatrixAuthorizationStrategy.java
index 2cfa6920..615c118e 100644
--- a/src/main/java/com/microsoft/jenkins/azuread/AzureAdMatrixAuthorizationStrategy.java
+++ b/src/main/java/com/microsoft/jenkins/azuread/AzureAdMatrixAuthorizationStrategy.java
@@ -1,249 +1,249 @@
-/*
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See LICENSE file in the project root for license information.
- */
-
-package com.microsoft.jenkins.azuread;
-
-import com.cloudbees.hudson.plugins.folder.AbstractFolder;
-import com.microsoft.azure.PagedList;
-import com.microsoft.azure.management.Azure;
-import com.microsoft.azure.management.graphrbac.GraphErrorException;
-import com.microsoft.azure.management.graphrbac.implementation.ADGroupInner;
-import com.microsoft.azure.management.graphrbac.implementation.UserInner;
-import hudson.Extension;
-import hudson.init.InitMilestone;
-import hudson.init.Initializer;
-import hudson.model.AbstractItem;
-import hudson.model.AutoCompletionCandidates;
-import hudson.model.Descriptor;
-import hudson.model.Item;
-import hudson.model.ItemGroup;
-import hudson.model.Job;
-import hudson.security.ACL;
-import hudson.security.AuthorizationStrategy;
-import hudson.security.GlobalMatrixAuthorizationStrategy;
-import hudson.security.Permission;
-import hudson.security.SecurityRealm;
-import hudson.security.SidACL;
-import jenkins.model.Jenkins;
-import org.acegisecurity.Authentication;
-import org.apache.commons.lang.StringUtils;
-import org.jenkinsci.plugins.matrixauth.AuthorizationContainer;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.DoNotUse;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
-import org.kohsuke.stapler.QueryParameter;
-
-import javax.annotation.Nonnull;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.logging.Logger;
-
-public class AzureAdMatrixAuthorizationStrategy extends GlobalMatrixAuthorizationStrategy {
-
- private static final Logger LOGGER = Logger.getLogger(AzureAdMatrixAuthorizationStrategy.class.getName());
-
- private final transient ObjId2FullSidMap objId2FullSidMap = new ObjId2FullSidMap();
-
- //
- // Inheriting from ProjectMatrixAuthorizationStrategy will lead to conflict
- // of AzureAdAuthorizationMatrixProperty and AuthorizationMatrixProperty.
- // Copy codes instead.
- //
- @Override
- @Nonnull
- public ACL getACL(@Nonnull Job, ?> project) {
- AzureAdAuthorizationMatrixProperty amp = project.getProperty(AzureAdAuthorizationMatrixProperty.class);
- if (amp != null) {
- return amp.getInheritanceStrategy().getEffectiveACL(amp.getACL(), project);
- } else {
- return getACL(project.getParent());
- }
- }
-
- @Restricted(NoExternalUse.class)
- public static ACL inheritingACL(final ACL parent, final ACL child) {
- if (parent instanceof SidACL && child instanceof SidACL) {
- return ((SidACL) child).newInheritingACL((SidACL) parent);
- }
- return new ACL() {
- @Override
- public boolean hasPermission(@Nonnull Authentication a, @Nonnull Permission permission) {
- return a.equals(SYSTEM) || child.hasPermission(a, permission) || parent.hasPermission(a, permission);
- }
- };
- }
-
- public ACL getACL(ItemGroup> g) {
- if (g instanceof Item) {
- Item item = (Item) g;
- return item.getACL();
- }
- return getRootACL();
- }
-
- @Override
- @Nonnull
- public ACL getACL(@Nonnull AbstractItem item) {
- if (Jenkins.get().getPlugin("cloudbees-folder") != null) { // optional dependency
- if (item instanceof AbstractFolder) {
- AzureAdAuthorizationMatrixFolderProperty p =
- ((AbstractFolder>) item).getProperties().get(AzureAdAuthorizationMatrixFolderProperty.class);
- if (p != null) {
- return p.getInheritanceStrategy().getEffectiveACL(p.getACL(), item);
- }
- }
- }
- return getACL(item.getParent());
- }
-
- @Override
- @Nonnull
- @SuppressRestrictedWarnings(value = {IdStrategyComparator.class, AuthorizationContainer.class})
- public Set getGroups() {
- Set r = new TreeSet<>(new IdStrategyComparator());
- r.addAll(super.getGroups());
- for (Job, ?> j : Jenkins.get().getAllItems(Job.class)) {
- AzureAdAuthorizationMatrixProperty jobProperty = j.getProperty(AzureAdAuthorizationMatrixProperty.class);
- if (jobProperty != null) {
- r.addAll(jobProperty.getGroups());
- }
- }
- for (AbstractFolder> j : Jenkins.get().getAllItems(AbstractFolder.class)) {
- AzureAdAuthorizationMatrixFolderProperty folderProperty =
- j.getProperties().get(AzureAdAuthorizationMatrixFolderProperty.class);
- if (folderProperty != null) {
- r.addAll(folderProperty.getGroups());
- }
- }
- return r;
- }
-
- // Copy ended
-
- @Override
- public void add(Permission p, String sid) {
- super.add(p, sid);
- objId2FullSidMap.putFullSid(sid);
- }
-
- @Override
- public boolean hasExplicitPermission(String sid, Permission p) {
- // Jenkins will pass in the object Id as sid
- final String objectId = sid;
- if (objectId == null) {
- return false;
- }
- return super.hasExplicitPermission(objId2FullSidMap.getOrOriginal(objectId), p);
- }
-
- @Override
- public boolean hasPermission(String sid, Permission p) {
- // Jenkins will pass in the object Id as sid
- final String objectId = sid;
- return super.hasPermission(objId2FullSidMap.getOrOriginal(objectId), p);
- }
-
- @Override
- public boolean hasPermission(String sid, Permission p, boolean principal) {
- // Jenkins will pass in the object Id as sid
- final String objectId = sid;
- return super.hasPermission(objId2FullSidMap.getOrOriginal(objectId), p, principal);
- }
-
- @Extension
- public static final Descriptor DESCRIPTOR = new DescriptorImpl();
-
-
- static AutoCompletionCandidates searchAndGenerateCandidates(String prefix) throws IOException {
- final int maxCandidates = 20;
- if (StringUtils.isEmpty(prefix)) {
- return null;
- }
-
- SecurityRealm realm = Jenkins.get().getSecurityRealm();
- if (!(realm instanceof AzureSecurityRealm)) {
- return null;
- }
- Azure.Authenticated authenticated = ((AzureSecurityRealm) realm).getAzureClient();
-
- List candidates = new ArrayList<>();
- LOGGER.info("search users with prefix: " + prefix);
- try {
- PagedList matchedUsers = authenticated.activeDirectoryUsers().inner()
- .list(String.format("startswith(displayName,'%s') or startswith(mail, '%s')", prefix, prefix));
- for (UserInner user : matchedUsers.currentPage().items()) {
- candidates.add(new AzureObject(user.objectId(), user.displayName()));
- if (candidates.size() > maxCandidates) {
- break;
- }
- }
-
- if (!matchedUsers.hasNextPage()) {
- LOGGER.info("search groups with prefix " + prefix);
- PagedList matchedGroups = authenticated.activeDirectoryGroups()
- .inner().list("startswith(displayName,'" + prefix + "')");
- for (ADGroupInner group : matchedGroups.currentPage().items()) {
- candidates.add(new AzureObject(group.objectId(), group.displayName()));
- if (candidates.size() > maxCandidates) {
- break;
- }
- }
- }
- } catch (GraphErrorException e) {
- LOGGER.warning("Do not have sufficient privileges to search related users or groups");
- }
-
- AutoCompletionCandidates c = new AutoCompletionCandidates();
- for (AzureObject obj : candidates) {
- String candidateText = ObjId2FullSidMap.generateFullSid(obj.getDisplayName(), obj.getObjectId());
- c.add(candidateText);
- }
- return c;
- }
-
- public static class DescriptorImpl extends GlobalMatrixAuthorizationStrategy.DescriptorImpl {
- @Override
- protected GlobalMatrixAuthorizationStrategy create() {
- return new AzureAdMatrixAuthorizationStrategy();
- }
-
- @Override
- @Nonnull
- public String getDisplayName() {
- return "Azure Active Directory Matrix-based security";
- }
-
- public AutoCompletionCandidates doAutoCompleteUserOrGroup(@QueryParameter String value) throws IOException {
- return searchAndGenerateCandidates(value);
- }
- }
-
- @Restricted(DoNotUse.class)
- @SuppressRestrictedWarnings(GlobalMatrixAuthorizationStrategy.ConverterImpl.class)
- public static class ConverterImpl extends GlobalMatrixAuthorizationStrategy.ConverterImpl {
- @Override
- public GlobalMatrixAuthorizationStrategy create() {
- return new AzureAdMatrixAuthorizationStrategy();
- }
-
- @Override
- @SuppressWarnings("rawtypes")
- public boolean canConvert(Class type) {
- return type == AzureAdMatrixAuthorizationStrategy.class;
- }
- }
-
- @Initializer(before = InitMilestone.PLUGINS_STARTED)
- public static void fixClassNameTypo() {
- // before 0.2.0
- Jenkins.XSTREAM2.addCompatibilityAlias(
- "com.microsoft.jenkins.azuread.AzureAdMatrixAuthorizationStategy",
- AzureAdMatrixAuthorizationStrategy.class);
- }
-}
+/*
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE file in the project root for license information.
+ */
+
+package com.microsoft.jenkins.azuread;
+
+import com.cloudbees.hudson.plugins.folder.AbstractFolder;
+import com.microsoft.azure.PagedList;
+import com.microsoft.azure.management.Azure;
+import com.microsoft.azure.management.graphrbac.GraphErrorException;
+import com.microsoft.azure.management.graphrbac.implementation.ADGroupInner;
+import com.microsoft.azure.management.graphrbac.implementation.UserInner;
+import hudson.Extension;
+import hudson.init.InitMilestone;
+import hudson.init.Initializer;
+import hudson.model.AbstractItem;
+import hudson.model.AutoCompletionCandidates;
+import hudson.model.Descriptor;
+import hudson.model.Item;
+import hudson.model.ItemGroup;
+import hudson.model.Job;
+import hudson.security.ACL;
+import hudson.security.AuthorizationStrategy;
+import hudson.security.GlobalMatrixAuthorizationStrategy;
+import hudson.security.Permission;
+import hudson.security.SecurityRealm;
+import hudson.security.SidACL;
+import jenkins.model.Jenkins;
+import org.acegisecurity.Authentication;
+import org.apache.commons.lang.StringUtils;
+import org.jenkinsci.plugins.matrixauth.AuthorizationContainer;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.DoNotUse;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
+import org.kohsuke.stapler.QueryParameter;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Logger;
+
+public class AzureAdMatrixAuthorizationStrategy extends GlobalMatrixAuthorizationStrategy {
+
+ private static final Logger LOGGER = Logger.getLogger(AzureAdMatrixAuthorizationStrategy.class.getName());
+
+ private final transient ObjId2FullSidMap objId2FullSidMap = new ObjId2FullSidMap();
+
+ //
+ // Inheriting from ProjectMatrixAuthorizationStrategy will lead to conflict
+ // of AzureAdAuthorizationMatrixProperty and AuthorizationMatrixProperty.
+ // Copy codes instead.
+ //
+ @Override
+ @Nonnull
+ public ACL getACL(@Nonnull Job, ?> project) {
+ AzureAdAuthorizationMatrixProperty amp = project.getProperty(AzureAdAuthorizationMatrixProperty.class);
+ if (amp != null) {
+ return amp.getInheritanceStrategy().getEffectiveACL(amp.getACL(), project);
+ } else {
+ return getACL(project.getParent());
+ }
+ }
+
+ @Restricted(NoExternalUse.class)
+ public static ACL inheritingACL(final ACL parent, final ACL child) {
+ if (parent instanceof SidACL && child instanceof SidACL) {
+ return ((SidACL) child).newInheritingACL((SidACL) parent);
+ }
+ return new ACL() {
+ @Override
+ public boolean hasPermission(@Nonnull Authentication a, @Nonnull Permission permission) {
+ return a.equals(SYSTEM) || child.hasPermission(a, permission) || parent.hasPermission(a, permission);
+ }
+ };
+ }
+
+ public ACL getACL(ItemGroup> g) {
+ if (g instanceof Item) {
+ Item item = (Item) g;
+ return item.getACL();
+ }
+ return getRootACL();
+ }
+
+ @Override
+ @Nonnull
+ public ACL getACL(@Nonnull AbstractItem item) {
+ if (Jenkins.get().getPlugin("cloudbees-folder") != null) { // optional dependency
+ if (item instanceof AbstractFolder) {
+ AzureAdAuthorizationMatrixFolderProperty p =
+ ((AbstractFolder>) item).getProperties().get(AzureAdAuthorizationMatrixFolderProperty.class);
+ if (p != null) {
+ return p.getInheritanceStrategy().getEffectiveACL(p.getACL(), item);
+ }
+ }
+ }
+ return getACL(item.getParent());
+ }
+
+ @Override
+ @Nonnull
+ @SuppressRestrictedWarnings(value = {IdStrategyComparator.class, AuthorizationContainer.class})
+ public Set getGroups() {
+ Set r = new TreeSet<>(new IdStrategyComparator());
+ r.addAll(super.getGroups());
+ for (Job, ?> j : Jenkins.get().getAllItems(Job.class)) {
+ AzureAdAuthorizationMatrixProperty jobProperty = j.getProperty(AzureAdAuthorizationMatrixProperty.class);
+ if (jobProperty != null) {
+ r.addAll(jobProperty.getGroups());
+ }
+ }
+ for (AbstractFolder> j : Jenkins.get().getAllItems(AbstractFolder.class)) {
+ AzureAdAuthorizationMatrixFolderProperty folderProperty =
+ j.getProperties().get(AzureAdAuthorizationMatrixFolderProperty.class);
+ if (folderProperty != null) {
+ r.addAll(folderProperty.getGroups());
+ }
+ }
+ return r;
+ }
+
+ // Copy ended
+
+ @Override
+ public void add(Permission p, String sid) {
+ super.add(p, sid);
+ objId2FullSidMap.putFullSid(sid);
+ }
+
+ @Override
+ public boolean hasExplicitPermission(String sid, Permission p) {
+ // Jenkins will pass in the object Id as sid
+ final String objectId = sid;
+ if (objectId == null) {
+ return false;
+ }
+ return super.hasExplicitPermission(objId2FullSidMap.getOrOriginal(objectId), p);
+ }
+
+ @Override
+ public boolean hasPermission(String sid, Permission p) {
+ // Jenkins will pass in the object Id as sid
+ final String objectId = sid;
+ return super.hasPermission(objId2FullSidMap.getOrOriginal(objectId), p);
+ }
+
+ @Override
+ public boolean hasPermission(String sid, Permission p, boolean principal) {
+ // Jenkins will pass in the object Id as sid
+ final String objectId = sid;
+ return super.hasPermission(objId2FullSidMap.getOrOriginal(objectId), p, principal);
+ }
+
+ @Extension
+ public static final Descriptor DESCRIPTOR = new DescriptorImpl();
+
+
+ static AutoCompletionCandidates searchAndGenerateCandidates(String prefix) throws IOException {
+ final int maxCandidates = 20;
+ if (StringUtils.isEmpty(prefix)) {
+ return null;
+ }
+
+ SecurityRealm realm = Jenkins.get().getSecurityRealm();
+ if (!(realm instanceof AzureSecurityRealm)) {
+ return null;
+ }
+ Azure.Authenticated authenticated = ((AzureSecurityRealm) realm).getAzureClient();
+
+ List candidates = new ArrayList<>();
+ LOGGER.info("search users with prefix: " + prefix);
+ try {
+ PagedList matchedUsers = authenticated.activeDirectoryUsers().inner()
+ .list(String.format("startswith(displayName,'%s') or startswith(mail, '%s')", prefix, prefix));
+ for (UserInner user : matchedUsers.currentPage().items()) {
+ candidates.add(new AzureObject(user.objectId(), user.displayName()));
+ if (candidates.size() > maxCandidates) {
+ break;
+ }
+ }
+
+ if (!matchedUsers.hasNextPage()) {
+ LOGGER.info("search groups with prefix " + prefix);
+ PagedList matchedGroups = authenticated.activeDirectoryGroups()
+ .inner().list("startswith(displayName,'" + prefix + "')");
+ for (ADGroupInner group : matchedGroups.currentPage().items()) {
+ candidates.add(new AzureObject(group.objectId(), group.displayName()));
+ if (candidates.size() > maxCandidates) {
+ break;
+ }
+ }
+ }
+ } catch (GraphErrorException e) {
+ LOGGER.warning("Do not have sufficient privileges to search related users or groups");
+ }
+
+ AutoCompletionCandidates c = new AutoCompletionCandidates();
+ for (AzureObject obj : candidates) {
+ String candidateText = ObjId2FullSidMap.generateFullSid(obj.getDisplayName(), obj.getObjectId());
+ c.add(candidateText);
+ }
+ return c;
+ }
+
+ public static class DescriptorImpl extends GlobalMatrixAuthorizationStrategy.DescriptorImpl {
+ @Override
+ protected GlobalMatrixAuthorizationStrategy create() {
+ return new AzureAdMatrixAuthorizationStrategy();
+ }
+
+ @Override
+ @Nonnull
+ public String getDisplayName() {
+ return "Azure Active Directory Matrix-based security";
+ }
+
+ public AutoCompletionCandidates doAutoCompleteUserOrGroup(@QueryParameter String value) throws IOException {
+ return searchAndGenerateCandidates(value);
+ }
+ }
+
+ @Restricted(DoNotUse.class)
+ @SuppressRestrictedWarnings(GlobalMatrixAuthorizationStrategy.ConverterImpl.class)
+ public static class ConverterImpl extends GlobalMatrixAuthorizationStrategy.ConverterImpl {
+ @Override
+ public GlobalMatrixAuthorizationStrategy create() {
+ return new AzureAdMatrixAuthorizationStrategy();
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public boolean canConvert(Class type) {
+ return type == AzureAdMatrixAuthorizationStrategy.class;
+ }
+ }
+
+ @Initializer(before = InitMilestone.PLUGINS_STARTED)
+ public static void fixClassNameTypo() {
+ // before 0.2.0
+ Jenkins.XSTREAM2.addCompatibilityAlias(
+ "com.microsoft.jenkins.azuread.AzureAdMatrixAuthorizationStategy",
+ AzureAdMatrixAuthorizationStrategy.class);
+ }
+}
diff --git a/src/main/java/com/microsoft/jenkins/azuread/AzureAdUser.java b/src/main/java/com/microsoft/jenkins/azuread/AzureAdUser.java
index 0bd21584..f1108fc3 100644
--- a/src/main/java/com/microsoft/jenkins/azuread/AzureAdUser.java
+++ b/src/main/java/com/microsoft/jenkins/azuread/AzureAdUser.java
@@ -118,26 +118,42 @@ public void setAuthorities(Collection groups) {
@SuppressWarnings({"checkstyle:needbraces"})
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
AzureAdUser that = (AzureAdUser) o;
- if (!Objects.equals(name, that.name)) return false;
- if (!Objects.equals(givenName, that.givenName)) return false;
- if (!Objects.equals(familyName, that.familyName)) return false;
- if (!Objects.equals(uniqueName, that.uniqueName)) return false;
- if (!Objects.equals(tenantID, that.tenantID)) return false;
+ if (!Objects.equals(name, that.name)) {
+ return false;
+ }
+ if (!Objects.equals(givenName, that.givenName)) {
+ return false;
+ }
+ if (!Objects.equals(familyName, that.familyName)) {
+ return false;
+ }
+ if (!Objects.equals(uniqueName, that.uniqueName)) {
+ return false;
+ }
+ if (!Objects.equals(tenantID, that.tenantID)) {
+ return false;
+ }
if (groupOIDs != null && that.groupOIDs != null) {
- if (!CollectionUtils.isEqualCollection(groupOIDs, that.groupOIDs)) return false;
+ if (!CollectionUtils.isEqualCollection(groupOIDs, that.groupOIDs)) {
+ return false;
+ }
} else if (groupOIDs != null || that.groupOIDs != null) {
return false;
}
return objectID.equals(that.objectID);
}
- @SuppressWarnings({"checkstyle:magicnumber"})
@Override
+ @SuppressWarnings("checkstyle:magicnumber")
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (givenName != null ? givenName.hashCode() : 0);
diff --git a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureJsonTokenExtractor.java b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureJsonTokenExtractor.java
index cda644bd..9fc7baac 100644
--- a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureJsonTokenExtractor.java
+++ b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureJsonTokenExtractor.java
@@ -1,36 +1,36 @@
-/*
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See LICENSE file in the project root for license information.
- */
-
-package com.microsoft.jenkins.azuread.scribe;
-
-import com.github.scribejava.core.exceptions.OAuthException;
-import com.github.scribejava.core.extractors.TokenExtractor;
-import com.github.scribejava.core.model.OAuth2AccessToken;
-import com.github.scribejava.core.model.Response;
-import com.github.scribejava.core.utils.Preconditions;
-import com.microsoft.jenkins.azuread.Utils;
-
-import java.io.IOException;
-
-public class AzureJsonTokenExtractor implements TokenExtractor {
-
- private static class InstanceHolder {
- private static final AzureJsonTokenExtractor INSTANCE = new AzureJsonTokenExtractor();
- }
-
- public static AzureJsonTokenExtractor instance() {
- return InstanceHolder.INSTANCE;
- }
-
- protected AzureJsonTokenExtractor() {
- }
-
- @Override
- public OAuth2AccessToken extract(Response response) throws IOException, OAuthException {
- Preconditions.checkEmptyString(response.getBody(),
- "Response body is incorrect. Can't extract a token from an empty string");
- return Utils.JsonUtil.fromJson(response.getBody(), AzureToken.class);
- }
-}
+/*
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE file in the project root for license information.
+ */
+
+package com.microsoft.jenkins.azuread.scribe;
+
+import com.github.scribejava.core.exceptions.OAuthException;
+import com.github.scribejava.core.extractors.TokenExtractor;
+import com.github.scribejava.core.model.OAuth2AccessToken;
+import com.github.scribejava.core.model.Response;
+import com.github.scribejava.core.utils.Preconditions;
+import com.microsoft.jenkins.azuread.Utils;
+
+import java.io.IOException;
+
+public class AzureJsonTokenExtractor implements TokenExtractor {
+
+ private static class InstanceHolder {
+ private static final AzureJsonTokenExtractor INSTANCE = new AzureJsonTokenExtractor();
+ }
+
+ public static AzureJsonTokenExtractor instance() {
+ return InstanceHolder.INSTANCE;
+ }
+
+ protected AzureJsonTokenExtractor() {
+ }
+
+ @Override
+ public OAuth2AccessToken extract(Response response) throws IOException, OAuthException {
+ Preconditions.checkEmptyString(response.getBody(),
+ "Response body is incorrect. Can't extract a token from an empty string");
+ return Utils.JsonUtil.fromJson(response.getBody(), AzureToken.class);
+ }
+}
diff --git a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureOAuthService.java b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureOAuthService.java
index 35e69cc1..d57229bf 100644
--- a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureOAuthService.java
+++ b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureOAuthService.java
@@ -1,58 +1,58 @@
-/*
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See LICENSE file in the project root for license information.
- */
-
-package com.microsoft.jenkins.azuread.scribe;
-
-import com.github.scribejava.core.model.OAuth2AccessToken;
-import com.github.scribejava.core.model.OAuthConfig;
-import com.github.scribejava.core.model.OAuthConstants;
-import com.github.scribejava.core.model.OAuthRequest;
-import com.github.scribejava.core.oauth.OAuth20Service;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
-
-public class AzureOAuthService extends OAuth20Service {
-
- public AzureOAuthService(AzureApi api, OAuthConfig config) {
- super(api, config);
- }
-
- @Override
- protected OAuthRequest createAccessTokenRequest(String code) {
- OAuthRequest request = super.createAccessTokenRequest(code);
- request.addParameter("resource", getApi().getResource());
- return request;
- }
-
- protected OAuthRequest createAccessTokenCredentialGrantRequest() {
- final OAuthRequest request = new OAuthRequest(getApi().getAccessTokenVerb(), getApi().getAccessTokenEndpoint());
- final OAuthConfig config = getConfig();
- request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
- request.addParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
- request.addParameter(OAuthConstants.SCOPE, config.getScope());
- request.addParameter(OAuthConstants.GRANT_TYPE, "client_credentials");
- return request;
- }
-
- public OAuth2AccessToken getAccessTokenCredentialGrant()
- throws InterruptedException, ExecutionException, IOException {
- final OAuthRequest request = createAccessTokenCredentialGrantRequest();
- return sendAccessTokenRequestSync(request);
- }
-
- @Override
- public AzureApi getApi() {
- return (AzureApi) super.getApi();
- }
-
- public final String getLogoutUrl() {
- return getLogoutUrl(null);
- }
-
- public String getLogoutUrl(String postLogoutUrl) {
- return getApi().getLogoutUrl(postLogoutUrl);
- }
-}
+/*
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE file in the project root for license information.
+ */
+
+package com.microsoft.jenkins.azuread.scribe;
+
+import com.github.scribejava.core.model.OAuth2AccessToken;
+import com.github.scribejava.core.model.OAuthConfig;
+import com.github.scribejava.core.model.OAuthConstants;
+import com.github.scribejava.core.model.OAuthRequest;
+import com.github.scribejava.core.oauth.OAuth20Service;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class AzureOAuthService extends OAuth20Service {
+
+ public AzureOAuthService(AzureApi api, OAuthConfig config) {
+ super(api, config);
+ }
+
+ @Override
+ protected OAuthRequest createAccessTokenRequest(String code) {
+ OAuthRequest request = super.createAccessTokenRequest(code);
+ request.addParameter("resource", getApi().getResource());
+ return request;
+ }
+
+ protected OAuthRequest createAccessTokenCredentialGrantRequest() {
+ final OAuthRequest request = new OAuthRequest(getApi().getAccessTokenVerb(), getApi().getAccessTokenEndpoint());
+ final OAuthConfig config = getConfig();
+ request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
+ request.addParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
+ request.addParameter(OAuthConstants.SCOPE, config.getScope());
+ request.addParameter(OAuthConstants.GRANT_TYPE, "client_credentials");
+ return request;
+ }
+
+ public OAuth2AccessToken getAccessTokenCredentialGrant()
+ throws InterruptedException, ExecutionException, IOException {
+ final OAuthRequest request = createAccessTokenCredentialGrantRequest();
+ return sendAccessTokenRequestSync(request);
+ }
+
+ @Override
+ public AzureApi getApi() {
+ return (AzureApi) super.getApi();
+ }
+
+ public final String getLogoutUrl() {
+ return getLogoutUrl(null);
+ }
+
+ public String getLogoutUrl(String postLogoutUrl) {
+ return getApi().getLogoutUrl(postLogoutUrl);
+ }
+}
diff --git a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureToken.java b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureToken.java
index 1c4a0077..d91c1b6a 100644
--- a/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureToken.java
+++ b/src/main/java/com/microsoft/jenkins/azuread/scribe/AzureToken.java
@@ -31,8 +31,8 @@ public AzureToken(@JsonProperty("access_token") String accessToken,
this.idToken = idToken;
}
- @SuppressWarnings({"checkstyle:magicnumber"})
@Override
+ @SuppressWarnings("checkstyle:magicnumber")
public int hashCode() {
int hash = super.hashCode();
hash = 41 * hash + Objects.hashCode(idToken);