Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.security.support.expressiondsl;

import org.elasticsearch.common.xcontent.ToXContentObject;

/**
* Implementations of this interface represent an expression used for user role mapping
* that can later be resolved to a boolean value.
*/
public interface RoleMapperExpression extends ToXContentObject {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.security.support.expressiondsl.expressions;

import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression;

/**
* An expression that evaluates to <code>true</code> if-and-only-if all its children
* evaluate to <code>true</code>.
* An <em>all</em> expression with no children is always <code>true</code>.
*/
public final class AllExpression extends CompositeRoleMapperExpressionBase {

public AllExpression(final RoleMapperExpression...elements) {
super("all", elements);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.security.support.expressiondsl.expressions;

import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression;

/**
* An expression that evaluates to <code>true</code> if at least one of its children
* evaluate to <code>true</code>.
* An <em>any</em> expression with no children is never <code>true</code>.
*/
public final class AnyExpression extends CompositeRoleMapperExpressionBase {

public AnyExpression(final RoleMapperExpression... elements) {
super("any", elements);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.security.support.expressiondsl.expressions;

import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression;
import org.elasticsearch.client.security.support.expressiondsl.fields.DnFieldExpression;
import org.elasticsearch.client.security.support.expressiondsl.fields.FieldExpressionBuilder;
import org.elasticsearch.client.security.support.expressiondsl.fields.GroupsFieldExpression;
import org.elasticsearch.client.security.support.expressiondsl.fields.MetadataFieldExpression;
import org.elasticsearch.client.security.support.expressiondsl.fields.UsernameFieldExpression;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

/**
* Builder for composite role mapper expressions.<br>
* Example usage:
* <pre>
* {@code
* final AllExpression allExpression = CompositeExpressionBuilder.builder(AllExpression.class)
.addExpression(CompositeExpressionBuilder.builder(AnyExpression.class)
.addExpression(FieldExpressionBuilder.builder(DnFieldExpression.class)
.addValue("*,ou=admin,dc=example,dc=com")
.build())
.addExpression(FieldExpressionBuilder.builder(UsernameFieldExpression.class)
.addValue("es-admin").addValue("es-system")
.build())
.build())
.addExpression(FieldExpressionBuilder.builder(GroupsFieldExpression.class)
.addValue("cn=people,dc=example,dc=com")
.build())
.addExpression(new ExceptExpression(FieldExpressionBuilder.builder(MetadataFieldExpression.class)
.withKey("metadata.terminated_date")
.addValue(new Date())
.build()))
.build();
* }
* </pre>
*/
public final class CompositeExpressionBuilder<T extends CompositeRoleMapperExpressionBase> {
private Constructor<T> ctor;
private List<RoleMapperExpression> elements = new ArrayList<>();

private CompositeExpressionBuilder(Class<T> clazz) {
try {
this.ctor = clazz.getConstructor(RoleMapperExpression[].class);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}

public static <T extends CompositeRoleMapperExpressionBase> CompositeExpressionBuilder<T> builder(Class<T> clazz) {
return new CompositeExpressionBuilder<>(clazz);
}

public CompositeExpressionBuilder<T> addExpression(final RoleMapperExpression expression) {
assert expression != null : "expression cannot be null";
elements.add(expression);
return this;
}

public T build() {
try {
return ctor.newInstance(new Object[] { elements.toArray(new RoleMapperExpression[0]) });
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should be using reflection for this.
There's only 2 classes to handle, we can do it more simply and safely with a Function<RoleMapperExpression[], T>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have refactored the code to simplify this. Thank you.

} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.security.support.expressiondsl.expressions;

import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
* Expression of role mapper expressions which can be combined by operations like AND, OR
*/
public abstract class CompositeRoleMapperExpressionBase implements RoleMapperExpression {
private final String name;
private final List<RoleMapperExpression> elements;

public CompositeRoleMapperExpressionBase(final String name, final RoleMapperExpression... elements) {
assert name != null : "field name cannot be null";
assert elements != null : "at least one field expression is required";
this.name = name;
this.elements = Collections.unmodifiableList(Arrays.asList(elements));
}

public String getName() {
return this.getName();
}

public List<RoleMapperExpression> getElements() {
return elements;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

final CompositeRoleMapperExpressionBase that = (CompositeRoleMapperExpressionBase) o;
if (Objects.equals(this.getName(), that.getName()) == false) {
return false;
}
return Objects.equals(this.getElements(), that.getElements());
}

@Override
public int hashCode() {
return Objects.hash(name, elements);
}

@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
builder.startObject();
builder.startArray(name);
for (RoleMapperExpression e : elements) {
e.toXContent(builder, params);
}
builder.endArray();
return builder.endObject();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.security.support.expressiondsl.expressions;

import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;

/**
* A negating expression. That is, this expression evaluates to <code>true</code> if-and-only-if
* its delegate expression evaluate to <code>false</code>.
* Syntactically, <em>except</em> expressions are intended to be children of <em>all</em>
* expressions ({@link AllExpression}).
*/
public final class ExceptExpression implements RoleMapperExpression {

public static final String NAME = "except";

private final RoleMapperExpression expression;

public ExceptExpression(final RoleMapperExpression expression) {
assert expression != null : "expression cannot be null";
this.expression = expression;
}

public RoleMapperExpression getInnerExpression() {
return expression;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

final ExceptExpression that = (ExceptExpression) o;
return this.expression.equals(that.expression);
}

@Override
public int hashCode() {
return expression.hashCode();
}

@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
builder.startObject();
builder.field(NAME);
expression.toXContent(builder, params);
return builder.endObject();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.security.support.expressiondsl.fields;

public final class DnFieldExpression extends FieldExpressionBase {

public DnFieldExpression(final Object[] values) {
super("dn", values);
}

}
Loading