Skip to content
Draft
Show file tree
Hide file tree
Changes from 10 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
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ charset = utf-8
[*.properties]
charset = latin1

[*.{yaml, yml}]
[*.{ts, tsx, js, jsx, json, yaml, yml, babelrc}]
indent_size = 2
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,13 @@ com_crashlytics_export_strings.xml

# JMH benchmark reports
jmh-report.json

# Node.js modules
node_modules

# Webpack build output
src/main/webapp/js
npm-debug.log

# node binaries from frontend-maven-plugin
node/
Empty file added .mvn_exec_node
Empty file.
5,651 changes: 5,651 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "folder-auth-plugin",
"version": "1.0.0",
"description": "Jenkins Folder Auth plugin",
"private": true,
"main": "index.js",
"directories": {
"doc": "docs"
},
"scripts": {
"mvnbuild": "npm run build",
"build": "webpack --mode production",
"build:dev": "webpack --mode development --watch",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/jenkinsci/folder-auth-plugin.git"
},
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/jenkinsci/folder-auth-plugin/issues"
},
"homepage": "https://github.com/jenkinsci/folder-auth-plugin#readme",
"dependencies": {
"bootstrap": "^4.5.0",
"react": "^16.13.1",
"react-bootstrap": "^1.0.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@babel/preset-react": "^7.9.4",
"@types/react": "^16.9.35",
"@types/react-bootstrap": "^1.0.1",
"@types/react-dom": "^16.9.8",
"babel-loader": "^8.1.0",
"css-loader": "^3.5.3",
"source-map-loader": "^0.2.4",
"style-loader": "^1.2.1",
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
}
}
31 changes: 29 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>3.55</version>
<version>4.2</version>
<relativePath/>
</parent>

Expand All @@ -27,9 +27,11 @@
<properties>
<revision>1.3</revision>
<changelist>-SNAPSHOT</changelist>
<jenkins.version>2.164.1</jenkins.version>
<jenkins.version>2.235</jenkins.version>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also needs a version bump to at least 2.235

<java.level>8</java.level>
<configuration-as-code.version>1.35</configuration-as-code.version>
<node.version>12.16.3</node.version>
<npm.version>6.14.5</npm.version>
</properties>

<repositories>
Expand Down Expand Up @@ -81,5 +83,30 @@
<version>${configuration-as-code.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.10.0</version>
</plugin>
</plugins>
</build>
</project>
50 changes: 50 additions & 0 deletions src/main/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import { FunctionComponent, useEffect, useState } from 'react';
import Tab from 'react-bootstrap/Tab';
import Tabs from 'react-bootstrap/Tabs';

import RoleType from './model/RoleType';

import './css/App.css';
import 'bootstrap/dist/css/bootstrap.min.css';

// @ts-ignore
const rootUrl = rootURL;
// @ts-ignore
const csrfCrumb = crumb.value;


const App: FunctionComponent = () => {
const [authorizationStrategy, setAuthorizationStrategy] = useState(null);

useEffect(() => {
(async () => {
const request = await fetch(`${rootUrl}/folder-auth/authorizationStrategy`, {
headers: {
'Jenkins-Crumb': csrfCrumb,
},
});
const data = await request.json();
setAuthorizationStrategy(data);
})().catch(err => {
throw new Error(`Unable to load authorization strategy: ${err}`);
});
}, []);

return (
<Tabs defaultActiveKey={RoleType.GLOBAL} id='role-type-tabs' variant='tabs'>
<Tab eventKey={RoleType.GLOBAL} title='Global Roles' tabClassName='tab'>
Hello world - Global Roles
</Tab>
<Tab eventKey={RoleType.FOLDER} title='Folder Roles' tabClassName='tab'>
Hello world - Folder Roles <br/>
{authorizationStrategy && JSON.stringify(authorizationStrategy)}
</Tab>
<Tab eventKey={RoleType.AGENT} title='Agent Roles' tabClassName='tab'>
Hello world - Agent Roles
</Tab>
</Tabs>
);
}

export default App;
5 changes: 5 additions & 0 deletions src/main/frontend/src/css/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
a.tab:link,
a.tab:visited {
color: #007bff;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hack to unset global a:link and a:visited.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a known PITA, I want to fix that

text-decoration: none !important;
}
9 changes: 9 additions & 0 deletions src/main/frontend/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';

import App from './App';
import { resetEnvironment } from './resetEnvironment';

resetEnvironment();
const root = document.getElementById('root');
ReactDOM.render(<App/>, root);
7 changes: 7 additions & 0 deletions src/main/frontend/src/model/RoleType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
enum RoleType {
GLOBAL,
FOLDER,
AGENT,
}

export default RoleType;
1 change: 1 addition & 0 deletions src/main/frontend/src/resetEnvironment.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export function resetEnvironment(): void;
14 changes: 14 additions & 0 deletions src/main/frontend/src/resetEnvironment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Reset JS environment set up by prototype.js to not interfere with
* our react components.
*
* @see https://github.com/jenkinsci/jenkins/blob/75468da366c1d257a51655dcbe952d55b8aeeb9c/war/src/main/js/util/jenkins.js#L22
*/
export function resetEnvironment() {
if (Array.prototype.toJSON) {
delete Array.prototype.toJSON;
delete Object.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package io.jenkins.plugins.folderauth;

import com.cloudbees.hudson.plugins.folder.AbstractFolder;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import hudson.Extension;
import hudson.model.AbstractItem;
import hudson.model.Api;
Expand All @@ -17,6 +20,7 @@
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import io.jenkins.plugins.folderauth.misc.AgentRoleCreationRequest;
import io.jenkins.plugins.folderauth.misc.FolderBasedAuthorizationStrategyWrapper;
import io.jenkins.plugins.folderauth.misc.FolderRoleCreationRequest;
import io.jenkins.plugins.folderauth.misc.GlobalRoleCreationRequest;
import io.jenkins.plugins.folderauth.misc.PermissionWrapper;
Expand All @@ -29,6 +33,7 @@
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.json.JsonBody;
Expand All @@ -39,6 +44,7 @@
import javax.annotation.ParametersAreNonnullByDefault;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
Expand All @@ -54,6 +60,7 @@
@ParametersAreNonnullByDefault
public class FolderAuthorizationStrategyManagementLink extends ManagementLink {
private static final Logger LOGGER = Logger.getLogger(FolderAuthorizationStrategyManagementLink.class.getName());
private static final JsonFactory jsonFactory = new JsonFactory();

@CheckForNull
@Override
Expand Down Expand Up @@ -413,6 +420,7 @@ public void doRemoveSidFromFolderRole(@QueryParameter(required = true) String ro
FolderAuthorizationStrategyAPI.removeSidFromFolderRole(sid, roleName);
redirect();
}

/**
* Removes {@code sid} from the agent role identified by {@code roleName}.
*
Expand All @@ -430,4 +438,23 @@ public void doRemoveSidFromAgentRole(@QueryParameter(required = true) String rol
FolderAuthorizationStrategyAPI.removeSidFromAgentRole(sid, roleName);
redirect();
}

@GET
@Restricted(NoExternalUse.class)
public void doAuthorizationStrategy(StaplerResponse response) throws IllegalStateException, IOException {
Jenkins jenkins = Jenkins.get();
jenkins.checkPermission(Jenkins.ADMINISTER);
AuthorizationStrategy strategy = jenkins.getAuthorizationStrategy();
if (!(strategy instanceof FolderBasedAuthorizationStrategy)) {
throw new IllegalStateException("Folder Based Authorization Strategy is not active.");
}
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Writer writer = response.getWriter();
FolderBasedAuthorizationStrategyWrapper wrapper = new FolderBasedAuthorizationStrategyWrapper((FolderBasedAuthorizationStrategy) strategy);
JsonGenerator generator = jsonFactory.createGenerator(writer);
Copy link
Member Author

@AbhyudayaSharma AbhyudayaSharma Jun 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

net.sf.json was able to convert the AuthorizationStrategy to JSON but during the process it was generating over 1000 lines of warnings. I think using Jackson for it is a better solution. Please let me know if there is some better alternative.

generator.setCodec(new ObjectMapper());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best to cache this, you only need to create one once

generator.writeObject(wrapper);
writer.flush();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.jenkins.plugins.folderauth.misc;

import io.jenkins.plugins.folderauth.FolderBasedAuthorizationStrategy;
import io.jenkins.plugins.folderauth.roles.AgentRole;
import io.jenkins.plugins.folderauth.roles.FolderRole;
import io.jenkins.plugins.folderauth.roles.GlobalRole;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Set;

@Restricted(NoExternalUse.class)
@ParametersAreNonnullByDefault
public class FolderBasedAuthorizationStrategyWrapper {
private final Set<GlobalRole> globalRoles;
private final Set<FolderRole> folderRoles;
private final Set<AgentRole> agentRoles;

public FolderBasedAuthorizationStrategyWrapper(FolderBasedAuthorizationStrategy strategy) {
this.globalRoles = strategy.getGlobalRoles();
this.folderRoles = strategy.getFolderRoles();
this.agentRoles = strategy.getAgentRoles();
}

public Set<GlobalRole> getGlobalRoles() {
return globalRoles;
}

public Set<FolderRole> getFolderRoles() {
return folderRoles;
}

public Set<AgentRole> getAgentRoles() {
return agentRoles;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.jenkins.plugins.folderauth.misc;

import com.fasterxml.jackson.annotation.JsonIgnore;
import hudson.PluginManager;
import hudson.security.Permission;
import io.jenkins.plugins.folderauth.Messages;
Expand Down Expand Up @@ -30,6 +31,7 @@ public final class PermissionWrapper implements Comparable<PermissionWrapper> {
private transient Permission permission;
private final String id;

@SuppressWarnings("deprecation")
@Restricted(NoExternalUse.class)
public static final Set<Permission> DANGEROUS_PERMISSIONS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Jenkins.RUN_SCRIPTS,
Expand All @@ -53,6 +55,11 @@ public String getId() {
return String.format("%s/%s", permission.group.getId(), permission.name);
}

@Restricted(NoExternalUse.class) // Restricted so JCasC does not pick it up
public String getPermissionId() {
return id;
}

/**
* Used to setup the permission when deserialized
*
Expand All @@ -72,6 +79,7 @@ private Object readResolve() {
* @return the permission corresponding to this {@link PermissionWrapper}
*/
@Nonnull
@JsonIgnore
public Permission getPermission() {
return permission;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.jenkins.plugins.folderauth.roles;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.jenkins.plugins.folderauth.misc.PermissionWrapper;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
Expand Down Expand Up @@ -91,6 +92,7 @@ public SortedSet<PermissionWrapper> getPermissions() {
* @see AbstractRole#getPermissions() when permissions are needed in a sorted order.
*/
@Nonnull
@JsonIgnore
public Set<PermissionWrapper> getPermissionsUnsorted() {
return Collections.unmodifiableSet(permissionWrappers);
}
Expand All @@ -111,7 +113,8 @@ public Set<String> getSids() {
* @return a sorted comma separated list of sids assigned to this role
*/
@Nonnull
@SuppressWarnings("unused") // used by index.jelly
@Deprecated
@JsonIgnore
public String getSidsCommaSeparated() {
String string = new TreeSet<>(sids).toString();
return string.substring(1, string.length() - 1);
Expand Down
Loading