Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions gateway-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,16 @@
<artifactId>api-ldap-client-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.api</groupId>
<artifactId>api-ldap-codec-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.api</groupId>
<artifactId>api-asn1-api</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.mina</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,11 @@ public String getLdapRolesLookupFilePath() {
return get(LDAP_ROLES_LOOKUP_FILE_PATH);
}

@Override
public String getLdapRolesLookupBypassControlOid() {
return get(LDAP_ROLES_LOOKUP_BYPASS_CONTROL_OID);
}

@Override
public boolean getGroupUIServicesOnHomepage() {
return getBoolean(KNOX_HOMEPAGE_GROUP_UI_SERVICES, DEFAULT_GROUP_UI_SERVICES);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.asn1.util.Oid;
import org.apache.directory.api.ldap.codec.api.LdapApiService;
import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
import org.apache.directory.api.ldap.model.cursor.Cursor;
import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
Expand All @@ -40,8 +43,10 @@
import org.apache.directory.server.core.partition.ldif.LdifPartition;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.knox.gateway.config.ConfigurationException;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.i18n.messages.MessagesFactory;
import org.apache.knox.gateway.services.ldap.control.RolesLookupBypassControlFactory;
import org.apache.knox.gateway.services.ldap.interceptor.InterceptorFactory;

import java.io.File;
Expand Down Expand Up @@ -71,6 +76,7 @@ public class KnoxLDAPServerManager {
private String baseDn;
// Collection of DNs for the proxied backend LDAP servers
private Set<String> baseDns;
private String rolesLookupBypassControlOid;

/**
* Initialize the LDAP server with the given configuration
Expand All @@ -88,6 +94,14 @@ public void initialize(GatewayConfig config) throws Exception {
this.port = config.getLDAPPort();
this.baseDn = config.getLDAPBaseDN();

// Get OID for roles lookup bypass control
rolesLookupBypassControlOid = gatewayConfig.getLdapRolesLookupBypassControlOid();
if (StringUtils.isNotBlank(rolesLookupBypassControlOid)) {
if (!Oid.isOid(rolesLookupBypassControlOid)) {
throw new ConfigurationException("Roles Lookup Bypass Control OID is not valid");
}
}

createInterceptors(config);

// Clean up previous run if it didn't shut down cleanly
Expand Down Expand Up @@ -135,6 +149,16 @@ public void start() throws Exception {
directoryService = new DefaultDirectoryService();
directoryService.setInstanceLayout(new InstanceLayout(workDir));

// Add RolesLookupBypassControlFactory
if (StringUtils.isNotBlank(rolesLookupBypassControlOid)) {
LdapApiService apiService = directoryService.getLdapCodecService();
if (apiService == null) {
apiService = LdapApiServiceFactory.getSingleton();
}
RolesLookupBypassControlFactory rolesLookupBypassControlFactory = new RolesLookupBypassControlFactory(apiService, rolesLookupBypassControlOid);
apiService.registerRequestControl(rolesLookupBypassControlFactory);
}

// Create SchemaManager
SchemaManager schemaManager = SchemaManagerFactory.createSchemaManager();
directoryService.setSchemaManager(schemaManager);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.knox.gateway.services.ldap.control;

import org.apache.directory.api.ldap.model.message.Control;

public interface RolesLookupBypassControl extends Control {
boolean isBypassRolesLookup();
void setBypassRolesLookup(boolean bypassRolesLookup);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.knox.gateway.services.ldap.control;

import org.apache.directory.api.asn1.Asn1Object;
import org.apache.directory.api.asn1.DecoderException;
import org.apache.directory.api.asn1.EncoderException;
import org.apache.directory.api.asn1.util.Asn1Buffer;
import org.apache.directory.api.ldap.codec.api.ControlDecorator;
import org.apache.directory.api.ldap.codec.api.LdapApiService;

import java.nio.ByteBuffer;

public class RolesLookupBypassControlDecorator extends ControlDecorator<RolesLookupBypassControl> implements RolesLookupBypassControl {

private final RolesLookupBypassControlFactory rolesLookupBypassControlFactory;

public RolesLookupBypassControlDecorator(LdapApiService codec, RolesLookupBypassControl decoratedControl, RolesLookupBypassControlFactory rolesLookupBypassControlFactory) {
super(codec, decoratedControl);
this.rolesLookupBypassControlFactory = rolesLookupBypassControlFactory;
}

@Override
public Asn1Object decode(byte[] bytes) throws DecoderException {
rolesLookupBypassControlFactory.decodeValue(getDecorated(), bytes);
return this;
}

@Override
public int computeLength() {
return 3; // Tag, Length, Value
}

@Override
public ByteBuffer encode(ByteBuffer byteBuffer) throws EncoderException {
Asn1Buffer asn1Buffer = new Asn1Buffer();
rolesLookupBypassControlFactory.encodeValue(asn1Buffer, getDecorated());

// reverse the byte ordering because Asn1Buffers store bytes in reverse
ByteBuffer factoryBuffer = asn1Buffer.getBytes();
int totalBytes = factoryBuffer.remaining();
byte[] factoryBytes = factoryBuffer.array();
for (int i = totalBytes - 1; i >= 0; i-- ) {
byteBuffer.put(factoryBytes[i]);
}

return byteBuffer;
}

@Override
public boolean isBypassRolesLookup() {
return getDecorated().isBypassRolesLookup();
}

@Override
public void setBypassRolesLookup(boolean bypassRolesLookup) {
getDecorated().setBypassRolesLookup(bypassRolesLookup);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.knox.gateway.services.ldap.control;

import org.apache.directory.api.asn1.DecoderException;
import org.apache.directory.api.asn1.util.Asn1Buffer;
import org.apache.directory.api.ldap.codec.api.AbstractControlFactory;
import org.apache.directory.api.ldap.codec.api.LdapApiService;
import org.apache.directory.api.ldap.model.message.Control;

public class RolesLookupBypassControlFactory extends AbstractControlFactory<RolesLookupBypassControl> {
public static final byte BOOLEAN_TAG_BYTE = 0x01;

public RolesLookupBypassControlFactory(LdapApiService codec, String oid) {
super(codec, oid);
}

@Override
public Control newControl() {
return new RolesLookupBypassControlDecorator(codec, new RolesLookupBypassControlImpl(oid), this);
}

@Override
public void decodeValue(Control control, byte[] controlBytes) throws DecoderException {
if (control instanceof RolesLookupBypassControl) {
RolesLookupBypassControl rolesLookupBypassControl = (RolesLookupBypassControl) control;
if (controlBytes == null || controlBytes.length < 3) {
throw new DecoderException("Invalid BER encoding for Boolean Control");
}

if (controlBytes[0] != BOOLEAN_TAG_BYTE) {
throw new DecoderException("Expected Boolean Tag (0x01), found: " + controlBytes[0]);
}

boolean value = (controlBytes[2] != 0x00);
rolesLookupBypassControl.setBypassRolesLookup(value);
} else {
throw new DecoderException("Cannot decode into " + control.getClass().getSimpleName() + ". Control must be instance of RolesLookupBypassControl.");
}
}

@Override
public void encodeValue(Asn1Buffer buffer, Control control) {
if (control instanceof RolesLookupBypassControl) {
RolesLookupBypassControl rolesLookupBypassControl = (RolesLookupBypassControl) control;

buffer.put(BOOLEAN_TAG_BYTE);
buffer.put((byte) 1); // Value is one byte long
buffer.put((byte) (rolesLookupBypassControl.isBypassRolesLookup() ? 0xFF : 0x00));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.knox.gateway.services.ldap.control;

import org.apache.directory.api.ldap.model.message.controls.AbstractControl;

public class RolesLookupBypassControlImpl extends AbstractControl implements RolesLookupBypassControl {
private boolean bypassRolesLookup;

public RolesLookupBypassControlImpl(String oid) {
super(oid);
}

@Override
public boolean isBypassRolesLookup() {
return bypassRolesLookup;
}

@Override
public void setBypassRolesLookup(boolean bypassRolesLookup) {
this.bypassRolesLookup = bypassRolesLookup;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
*/
package org.apache.knox.gateway.services.ldap.interceptor;

import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.ldap.model.cursor.ListCursor;
import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.entry.Value;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
import org.apache.directory.api.ldap.model.message.Control;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.name.Rdn;
import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
Expand All @@ -33,6 +35,7 @@
import org.apache.knox.gateway.services.ldap.LDAPRolesLookupService;
import org.apache.knox.gateway.services.ldap.LdapMessages;
import org.apache.knox.gateway.services.ldap.LdapUtils;
import org.apache.knox.gateway.services.ldap.control.RolesLookupBypassControl;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -46,14 +49,29 @@
*/
public class LDAPRolesLookupInterceptor extends BaseInterceptor {
private static final LdapMessages LOG = MessagesFactory.get(LdapMessages.class);

private final LDAPRolesLookupService rolesLookupService;
private final String rolesLookupBypassControlOid;

public LDAPRolesLookupInterceptor(LDAPRolesLookupService rolesLookupService) {
public LDAPRolesLookupInterceptor(LDAPRolesLookupService rolesLookupService, String rolesLookupBypassControlOid) {
this.rolesLookupService = rolesLookupService;
this.rolesLookupBypassControlOid = rolesLookupBypassControlOid;
}

@Override
public EntryFilteringCursor search(SearchOperationContext ctx) throws LdapException {
if (StringUtils.isNotBlank(rolesLookupBypassControlOid)) {
if (ctx.hasRequestControl(rolesLookupBypassControlOid)) {
Control control = ctx.getRequestControl(rolesLookupBypassControlOid);
if (control instanceof RolesLookupBypassControl) {
RolesLookupBypassControl rolesLookupBypassControl = (RolesLookupBypassControl) control;
if (rolesLookupBypassControl.isBypassRolesLookup()) {
return next(ctx);
}
}
}
}

final List<Entry> entries = new ArrayList<>();
try (EntryFilteringCursor cursor = next(ctx)) {
while (cursor.next()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package org.apache.knox.gateway.services.ldap.interceptor;

import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.asn1.util.Oid;
import org.apache.directory.server.core.api.interceptor.Interceptor;
import org.apache.knox.gateway.GatewayServer;
import org.apache.knox.gateway.config.GatewayConfig;
Expand All @@ -35,7 +37,16 @@ public Interceptor create(GatewayConfig gatewayConfig, String name, Map<String,
if (ldapRolesLookupService == null || !ldapRolesLookupService.enabled()) {
throw new ServiceLifecycleException("LDAP roles lookup service not found or disabled");
}
return new LDAPRolesLookupInterceptor(ldapRolesLookupService);

// Configure the OID used for the RolesLookupBypassControl until an official OID is available
String rolesLookupBypassControlOid = gatewayConfig.getLdapRolesLookupBypassControlOid();
if (StringUtils.isNotBlank(rolesLookupBypassControlOid)) {
if (!Oid.isOid(rolesLookupBypassControlOid)) {
rolesLookupBypassControlOid = null;
}
}

return new LDAPRolesLookupInterceptor(ldapRolesLookupService, rolesLookupBypassControlOid);
}

protected LDAPRolesLookupService getLDAPRolesLookupService() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.server.core.api.CoreSession;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.LdapPrincipal;
import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
import org.apache.directory.server.core.api.filtering.EntryFilteringCursorImpl;
Expand Down Expand Up @@ -63,11 +62,6 @@ public LdapBackend getBackend() {
return backend;
}

@Override
public void init(DirectoryService directoryService) throws LdapException {
super.init(directoryService);
}

@Override
public Entry lookup(LookupOperationContext ctx) throws LdapException {
Entry entry = next(ctx);
Expand Down
Loading