Skip to content
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

[WIP][JENKINS-58293] Folder based Authorization as a new AuthorizationStrategy #89

Closed

Conversation

AbhyudayaSharma
Copy link
Member

Unlike #87 this adds a new @Extension with Authorization Strategy

This is a work in progress pull request

@AbhyudayaSharma AbhyudayaSharma requested a review from a team July 2, 2019 12:37
@AbhyudayaSharma AbhyudayaSharma self-assigned this Jul 2, 2019
*
* @param globalRoles set of roles from which to calculate the permissions.
*/
GlobalAclImpl(Set<GlobalRole> globalRoles) {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a new implementation for global roles. When the object is created, it pre-computes all permissions available to the sid by going through all of the roles.

Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to do it in a separate pull request? From what I see, the existing strategy could benefit from it as well

for (GlobalRole role : globalRoles) {
Set<Permission> impliedPermissions = ConcurrentHashMap.newKeySet();

role.permissions.parallelStream()
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm using parallel streams here and a thread-safe set. Please let me know if I need to revert back to single-threaded streams.

protected Boolean hasPermission(Sid p, Permission permission) {
Set<Permission> permissions = permissionList.get(toString(p));
if (permission != null && permissions.contains(permission)) {
return true;
Copy link
Member Author

Choose a reason for hiding this comment

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

hasPermission should now run in constant time (O(1)) because getting a value from a map and checking if something is contained in a set are both O(1) operations.


// this is really bad.
// See https://github.com/jenkinsci/jenkins/blob/75468da366c1d257a51655dcbe952d55b8aeeb9c/war/src/main/js/util/jenkins.js#L22
const oldPrototype = Array.prototype.toJSON;
Copy link
Member Author

Choose a reason for hiding this comment

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

I've implemented the same fix in the link
What would be the URL to import and use the file directly?

*
* @param globalRoles set of roles from which to calculate the permissions.
*/
GlobalAclImpl(Set<GlobalRole> globalRoles) {
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to do it in a separate pull request? From what I see, the existing strategy could benefit from it as well

pom.xml Show resolved Hide resolved
@CheckForNull
@Override
public String getDisplayName() {
return "Folder Authorization Strategy";
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice to make it localizable for the final implementation

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

Maybe it even makes sense to consider having a separate plugin for the new strategy.
But the code can be split later if there is such decision.

CC @runzexia @Supun94, reviews are needed

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@ParametersAreNonnullByDefault
Copy link
Member

Choose a reason for hiding this comment

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

IIRC it is not going to play well for the transient field. SportBugs/FindBugs are not equipped to resolve such deserialization cases IIRC

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class GlobalAclImplTest {
Copy link
Member

Choose a reason for hiding this comment

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

JCasC compatibility autotest here would be really interesting

* Also remove `forEach` from JavaScript as it is not compatible with
old browsers
* Make the dependency on Cloudbees folder plugin optional
* Update Jelly UI
* Make GlobalRoleCreationRequest `@Restricted(NoExternalUse.class)`
* Refactor roles and ACLs into their own packages
* Update the Jelly UI a bit
@AbhyudayaSharma
Copy link
Member Author

AbhyudayaSharma commented Jul 8, 2019

The test fails because ConcurrentHashMap.newKeySet was refused to be marshalled:

Caused by: java.lang.UnsupportedOperationException: Refusing to marshal java.util.concurrent.ConcurrentHashMap$KeySetView for security reasons; see https://jenkins.io/redirect/class-filter/
[2019-07-08T08:11:12.508Z] 	at hudson.util.XStream2$BlacklistedTypesConverter.marshal(XStream2.java:546)
[2019-07-08T08:11:12.508Z] 	at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:69)
[2019-07-08T08:11:12.508Z] 	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:58)
[2019-07-08T08:11:12.508Z] 	at com.thoughtworks.xstream.core.AbstractReferenceMarshaller$1.convertAnother(AbstractReferenceMarshaller.java:84)
[2019-07-08T08:11:12.508Z] 	at hudson.util.RobustReflectionConverter.marshallField(RobustReflectionConverter.java:265)
[2019-07-08T08:11:12.508Z] 	at hudson.util.RobustReflectionConverter$2.writeField(RobustReflectionConverter.java:252)
[2019-07-08T08:11:12.508Z] 	... 38 more

@AbhyudayaSharma
Copy link
Member Author

Would it be fine to create a pull request to Jenkins core and add java.util.concurrent.ConcurrentHashMap$KeySetView here? Otherwise, there's also the ConcurrentSkipListMap that could've been used but it is not whitelisted either.

@oleg-nenashev
Copy link
Member

Would it be fine to create a pull request to Jenkins core and add java.util.concurrent.ConcurrentHashMap$KeySetView here? Otherwise, there's also the ConcurrentSkipListMap that could've been used but it is not whitelisted either.

You can whitelist it for the plugin .
https://jenkins.io/blog/2018/01/13/jep-200/#making-plugins-compatible-with-jenkins-2-102-or-above

@AbhyudayaSharma AbhyudayaSharma changed the title [WIP] Folder based Authorization as a new AuthorizationStrategy [WIP][JENKINS-58293] Folder based Authorization as a new AuthorizationStrategy Jul 9, 2019
* Also make AbstractRole `@Restricted(NoExternalUse.class)`
* Add some JavaDoc
@@ -0,0 +1 @@
java.util.concurrent.ConcurrentHashMap$KeySetView
Copy link
Member

Choose a reason for hiding this comment

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

Bad idea. Fix your code to store a simpler type instead, like an ArrayList.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi @jglick ! I had wanted to use a Set to avoid duplicates and for faster remove() operations. Also since there were tickets for concurrency issues in the Role Strategy Plugin, this seemed like the perfect option. Could you please let me know why you think this is a bad idea? Also as an alternative, would you approve of CopyOnWriteArraySet? remove()s will be slower but we'll get thread safety and avoid duplicates.

Copy link
Member

@jvz jvz Jul 9, 2019

Choose a reason for hiding this comment

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

Add a writeReplace() method to convert the class to a serialization proxy, then inside that proxy class create a readResolve() method to convert the proxy class back to the original class. I've come across this exact same whitelisting issue twice so far in the past year.

Edit: here's an example of how I worked around this issue: https://github.com/jenkinsci/support-core-plugin/blob/master/src/main/java/com/cloudbees/jenkins/support/filter/ContentMappings.java

Copy link
Member

Choose a reason for hiding this comment

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

faster remove() operations

This smells wrong. How often are you modifying this set anyway? If it is being persisted in XStream, then any collection overhead would be orders of magnitude less than disk I/O when the result is saved.

Could you please let me know why you think this is a bad idea?

Data stored by a plugin should use simple types. There are good reasons to create a custom ClassFilter whitelist in a plugin; this is not one of them.

Copy link
Member

Choose a reason for hiding this comment

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

* Use the CasC incremental
* Use AbstractFolders instead of Folders
@oleg-nenashev
Copy link
Member

@AbhyudayaSharma
Copy link
Member Author

Unrelated build failure:

[2019-07-11T17:38:10.998Z] win2012-cd60a0 was marked offline: Connection was broken: java.util.concurrent.TimeoutException: Ping started at 1562866450996 hasn't completed by 1562866690996
[2019-07-11T17:38:10.998Z] 	at hudson.remoting.PingThread.ping(PingThread.java:134)
[2019-07-11T17:38:10.998Z] 	at hudson.remoting.PingThread.run(PingThread.java:90)

@AbhyudayaSharma
Copy link
Member Author

Hi everyone! We decided to move this code to a new repository: https://github.com/jenkinsci/folder-auth-plugin . For the issues above, I'm creating new JIRA tickets. I would love to hear your feedback there. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants