All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graylog2.rest.resources.system.ldap.LdapResource Maven / Gradle / Ivy

There is a newer version: 5.2.7
Show newest version
/**
 * This file is part of Graylog.
 *
 * Graylog is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Graylog is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Graylog.  If not, see .
 */
package org.graylog2.rest.resources.system.ldap;

import com.codahale.metrics.annotation.Timed;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.ldap.client.api.LdapConnectionConfig;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.graylog2.audit.AuditEventTypes;
import org.graylog2.audit.jersey.AuditEvent;
import org.graylog2.audit.jersey.NoAuditEvent;
import org.graylog2.plugin.database.ValidationException;
import org.graylog2.rest.models.system.ldap.requests.LdapSettingsRequest;
import org.graylog2.rest.models.system.ldap.requests.LdapTestConfigRequest;
import org.graylog2.rest.models.system.ldap.responses.LdapSettingsResponse;
import org.graylog2.rest.models.system.ldap.responses.LdapTestConfigResponse;
import org.graylog2.security.TrustAllX509TrustManager;
import org.graylog2.security.ldap.LdapConnector;
import org.graylog2.security.ldap.LdapSettingsImpl;
import org.graylog2.security.ldap.LdapSettingsService;
import org.graylog2.shared.rest.resources.RestResource;
import org.graylog2.shared.security.RestPermissions;
import org.graylog2.shared.security.ldap.LdapEntry;
import org.graylog2.shared.security.ldap.LdapSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.shiro.authz.annotation.Logical.OR;

@RequiresAuthentication

@Api(value = "System/LDAP", description = "LDAP settings")
@Path("/system/ldap")
public class LdapResource extends RestResource {
    private static final Logger LOG = LoggerFactory.getLogger(LdapResource.class);

    @Inject
    private LdapSettingsService ldapSettingsService;

    @Inject
    private LdapSettingsImpl.Factory ldapSettingsFactory;

    @Inject
    private LdapConnector ldapConnector;

    @GET
    @Timed
    @RequiresPermissions(RestPermissions.LDAP_EDIT)
    @ApiOperation("Get the LDAP configuration if it is configured")
    @Path("/settings")
    @Produces(MediaType.APPLICATION_JSON)
    public LdapSettingsResponse getLdapSettings() {
        final LdapSettings ldapSettings = ldapSettingsService.load();
        if (ldapSettings == null) {
            return LdapSettingsResponse.emptyDisabled();
        }

        return LdapSettingsResponse.create(
                ldapSettings.isEnabled(),
                ldapSettings.getSystemUserName(),
                ldapSettings.isSystemPasswordSet(),
                ldapSettings.getUri(),
                ldapSettings.isUseStartTls(),
                ldapSettings.isTrustAllCertificates(),
                ldapSettings.isActiveDirectory(),
                ldapSettings.getSearchBase(),
                ldapSettings.getSearchPattern(),
                ldapSettings.getDisplayNameAttribute(),
                ldapSettings.getDefaultGroup(),
                ldapSettings.getGroupMapping(),
                ldapSettings.getGroupSearchBase(),
                ldapSettings.getGroupIdAttribute(),
                ldapSettings.getAdditionalDefaultGroups(),
                ldapSettings.getGroupSearchPattern());
    }

    @POST
    @Timed
    @RequiresPermissions(RestPermissions.LDAP_EDIT)
    @ApiOperation("Test LDAP Configuration")
    @Path("/test")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @NoAuditEvent("only used to test LDAP configuration")
    public LdapTestConfigResponse testLdapConfiguration(@ApiParam(name = "Configuration to test", required = true)
                                                        @Valid @NotNull LdapTestConfigRequest request) {
        final LdapConnectionConfig config = new LdapConnectionConfig();
        final URI ldapUri = request.ldapUri();
        config.setLdapHost(ldapUri.getHost());
        config.setLdapPort(ldapUri.getPort());
        config.setUseSsl(ldapUri.getScheme().startsWith("ldaps"));
        config.setUseTls(request.useStartTls());

        if (request.trustAllCertificates()) {
            config.setTrustManagers(new TrustAllX509TrustManager());
        }

        if (!isNullOrEmpty(request.systemUsername())) {
            config.setName(request.systemUsername());

            if (!isNullOrEmpty(request.systemPassword())) {
                // Use the given password for the connection test
                config.setCredentials(request.systemPassword());
            } else {
                // If the config request has a username but no password set, we have to use the password from the database.
                // This is because we don't expose the plain-text password through the API anymore and the settings form
                // doesn't submit a password.
                final LdapSettings ldapSettings = ldapSettingsService.load();
                if (ldapSettings != null && !isNullOrEmpty(ldapSettings.getSystemPassword())) {
                    config.setCredentials(ldapSettings.getSystemPassword());
                }
            }
        }

        LdapNetworkConnection connection = null;
        try {
            try {
                connection = ldapConnector.connect(config);
            } catch (LdapException e) {
                return LdapTestConfigResponse.create(false, false, false, Collections.emptyMap(), Collections.emptySet(), e.getMessage());
            }

            if (null == connection) {
                return LdapTestConfigResponse.create(false, false, false, Collections.emptyMap(), Collections.emptySet(), "Could not connect to LDAP server");
            }

            boolean connected = connection.isConnected();
            boolean systemAuthenticated = connection.isAuthenticated();

            // the web interface allows testing the connection only, in that case we can bail out early.
            if (request.testConnectOnly()) {
                return LdapTestConfigResponse.create(connected, systemAuthenticated, false, Collections.emptyMap(), Collections.emptySet());
            }

            String userPrincipalName = null;
            boolean loginAuthenticated = false;
            Map entryMap = Collections.emptyMap();
            String exception = null;
            Set groups = Collections.emptySet();
            try {
                final LdapEntry entry = ldapConnector.search(
                        connection,
                        request.searchBase(),
                        request.searchPattern(),
                        "*",
                        request.principal(),
                        request.activeDirectory(),
                        request.groupSearchBase(),
                        request.groupIdAttribute(),
                        request.groupSearchPattern());
                if (entry != null) {
                    userPrincipalName = entry.getBindPrincipal();
                    entryMap = entry.getAttributes();
                    groups = entry.getGroups();
                }
            } catch (CursorException | LdapException e) {
                exception = e.getMessage();
            }

            try {
                loginAuthenticated = ldapConnector.authenticate(connection, userPrincipalName, request.password());
            } catch (Exception e) {
                exception = e.getMessage();
            }

            return LdapTestConfigResponse.create(connected, systemAuthenticated, loginAuthenticated, entryMap, groups, exception);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (IOException e) {
                    LOG.warn("Unable to close LDAP connection.", e);
                }
            }
        }
    }

    @PUT
    @Timed
    @RequiresPermissions(RestPermissions.LDAP_EDIT)
    @ApiOperation("Update the LDAP configuration")
    @Path("/settings")
    @Consumes(MediaType.APPLICATION_JSON)
    @AuditEvent(type = AuditEventTypes.LDAP_CONFIGURATION_UPDATE)
    public void updateLdapSettings(@ApiParam(name = "JSON body", required = true)
                                   @Valid @NotNull LdapSettingsRequest request) throws ValidationException {
        // load the existing config, or create a new one. we only support having one, currently
        final LdapSettings ldapSettings = firstNonNull(ldapSettingsService.load(), ldapSettingsFactory.createEmpty());

        ldapSettings.setSystemUsername(request.systemUsername());
        ldapSettings.setSystemPassword(request.systemPassword());
        ldapSettings.setUri(request.ldapUri());
        ldapSettings.setUseStartTls(request.useStartTls());
        ldapSettings.setTrustAllCertificates(request.trustAllCertificates());
        ldapSettings.setActiveDirectory(request.activeDirectory());
        ldapSettings.setSearchPattern(request.searchPattern());
        ldapSettings.setSearchBase(request.searchBase());
        ldapSettings.setEnabled(request.enabled());
        ldapSettings.setDisplayNameAttribute(request.displayNameAttribute());
        ldapSettings.setDefaultGroup(request.defaultGroup());
        ldapSettings.setGroupMapping(request.groupMapping());
        ldapSettings.setGroupSearchBase(request.groupSearchBase());
        ldapSettings.setGroupIdAttribute(request.groupIdAttribute());
        ldapSettings.setGroupSearchPattern(request.groupSearchPattern());
        ldapSettings.setAdditionalDefaultGroups(request.additionalDefaultGroups());

        ldapSettingsService.save(ldapSettings);
    }

    @DELETE
    @Timed
    @RequiresPermissions(RestPermissions.LDAP_EDIT)
    @ApiOperation("Remove the LDAP configuration")
    @Path("/settings")
    @AuditEvent(type = AuditEventTypes.LDAP_CONFIGURATION_DELETE)
    public void deleteLdapSettings() {
        ldapSettingsService.delete();
    }

    @GET
    @ApiOperation(value = "Get the LDAP group to Graylog role mapping", notes = "The return value is a simple hash with keys being the LDAP group names and the values the corresponding Graylog role names.")
    @RequiresPermissions(value = {RestPermissions.LDAPGROUPS_READ, RestPermissions.LDAP_EDIT}, logical = OR)
    @Path("/settings/groups")
    @Produces(MediaType.APPLICATION_JSON)
    public Map readGroupMapping(){
        final LdapSettings ldapSettings = firstNonNull(ldapSettingsService.load(), ldapSettingsFactory.createEmpty());
        return ldapSettings.getGroupMapping();
    }

    @PUT
    @RequiresPermissions(value = {RestPermissions.LDAPGROUPS_EDIT, RestPermissions.LDAP_EDIT}, logical = OR)
    @ApiOperation(value = "Update the LDAP group to Graylog role mapping", notes = "Corresponds directly to the output of GET /system/ldap/settings/groups")
    @Path("/settings/groups")
    @Consumes(MediaType.APPLICATION_JSON)
    @AuditEvent(type = AuditEventTypes.LDAP_GROUP_MAPPING_UPDATE)
    public Response updateGroupMappingSettings(@ApiParam(name = "JSON body", required = true, value = "A hash in which the keys are the LDAP group names and values is the Graylog role name.")
                                   @NotNull Map groupMapping) throws ValidationException {
        final LdapSettings ldapSettings = firstNonNull(ldapSettingsService.load(), ldapSettingsFactory.createEmpty());

        ldapSettings.setGroupMapping(groupMapping);
        ldapSettingsService.save(ldapSettings);

        return Response.noContent().build();
    }

    @GET
    @ApiOperation(value = "Get the available LDAP groups", notes = "")
    @RequiresPermissions(RestPermissions.LDAPGROUPS_READ)
    @Path("/groups")
    @Produces(MediaType.APPLICATION_JSON)
    public Set readGroups(){
        final LdapSettings ldapSettings = firstNonNull(ldapSettingsService.load(), ldapSettingsFactory.createEmpty());

        if (!ldapSettings.isEnabled()) {
            throw new BadRequestException("LDAP is disabled.");
        }
        if (isNullOrEmpty(ldapSettings.getGroupSearchBase()) || isNullOrEmpty(ldapSettings.getGroupIdAttribute())) {
            throw new BadRequestException("LDAP group configuration settings are not set.");
        }

        final LdapConnectionConfig config = new LdapConnectionConfig();
        final URI ldapUri = ldapSettings.getUri();
        config.setLdapHost(ldapUri.getHost());
        config.setLdapPort(ldapUri.getPort());
        config.setUseSsl(ldapUri.getScheme().startsWith("ldaps"));
        config.setUseTls(ldapSettings.isUseStartTls());

        if (ldapSettings.isTrustAllCertificates()) {
            config.setTrustManagers(new TrustAllX509TrustManager());
        }

        if (!isNullOrEmpty(ldapSettings.getSystemUserName()) && !isNullOrEmpty(ldapSettings.getSystemPassword())) {
            config.setName(ldapSettings.getSystemUserName());
            config.setCredentials(ldapSettings.getSystemPassword());
        }

        try (LdapNetworkConnection connection = ldapConnector.connect(config)) {
            return ldapConnector.listGroups(connection,
                                            ldapSettings.getGroupSearchBase(),
                                            ldapSettings.getGroupSearchPattern(),
                                            ldapSettings.getGroupIdAttribute()
            );
        } catch (IOException | LdapException e) {
            LOG.error("Unable to retrieve available LDAP groups", e);
            throw new InternalServerErrorException("Unable to retrieve available LDAP groups", e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy