software.coolstuff.springframework.owncloud.service.impl.rest.AbstractOwncloudRestServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for software.coolstuff.springframework.owncloud.service.impl.rest.AbstractOwncloudRestServiceImpl.java

Source

/*-
 * #%L
 * owncloud-spring-boot-starter
 * %%
 * Copyright (C) 2016 - 2017 by the original Authors
 * %%
 * This program 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.
 * 
 * This program 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 this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
package software.coolstuff.springframework.owncloud.service.impl.rest;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.http.*;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import software.coolstuff.springframework.owncloud.exception.OwncloudStatusException;
import software.coolstuff.springframework.owncloud.exception.auth.OwncloudInvalidAuthenticationObjectException;
import software.coolstuff.springframework.owncloud.model.OwncloudUserDetails;
import software.coolstuff.springframework.owncloud.service.impl.OwncloudUtils;

import javax.annotation.PostConstruct;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;

import static lombok.AccessLevel.PROTECTED;

@RequiredArgsConstructor(access = PROTECTED)
@Slf4j
abstract class AbstractOwncloudRestServiceImpl implements OwncloudRestService {

    private static final String DEFAULT_PATH = "/ocs/v1.php";
    private static final String AUTHORIZATION_METHOD_PREFIX = "Basic ";

    private final RestTemplateBuilder restTemplateBuilder;
    private final OwncloudRestProperties properties;
    private final ResponseErrorHandler responseErrorHandler;

    @Getter(PROTECTED)
    MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private RestTemplate restTemplate;

    protected AbstractOwncloudRestServiceImpl(RestTemplateBuilder builder, OwncloudRestProperties properties) {
        this(builder, properties,
                new DefaultOwncloudResponseErrorHandler(SpringSecurityMessageSource.getAccessor()));
    }

    @PostConstruct
    public void afterPropertiesSet() throws Exception {
        URL locationURL = OwncloudRestUtils.checkAndConvertLocation(properties.getLocation());
        configureRestTemplate(locationURL);
    }

    private void configureRestTemplate(URL locationURL) throws MalformedURLException {
        String rootUri = OwncloudRestUtils.appendDefaultPath(locationURL, DEFAULT_PATH);
        log.info("Create the REST-Template to URI {} to be used with the authenticated User", rootUri);
        restTemplate = restTemplateBuilder.additionalMessageConverters(new FormHttpMessageConverter())
                .errorHandler(responseErrorHandler).rootUri(rootUri).build();
        Validate.notNull(restTemplate);
    }

    @Override
    public RestTemplate getRestTemplate() {
        return restTemplate;
    }

    protected HttpHeaders prepareHeadersWithBasicAuthorization() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (OwncloudUtils.isAuthenticationClassNotSupported(authentication.getClass())) {
            throw new OwncloudInvalidAuthenticationObjectException(authentication,
                    UsernamePasswordAuthenticationToken.class);
        }
        return OwncloudRestUtils.addAuthorizationHeader(authentication);
    }

    protected String getLocation() {
        return properties.getLocation();
    }

    protected HttpEntity<String> emptyEntity(String username, String password) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,
                password);
        HttpHeaders headers = OwncloudRestUtils.addAuthorizationHeader(authenticationToken);
        return new HttpEntity<>(headers);
    }

    protected HttpEntity<String> emptyEntity() {
        return new HttpEntity<>(prepareHeadersWithBasicAuthorization());
    }

    protected HttpEntity<MultiValueMap<String, String>> multiValuedEntity(Map<String, List<String>> data) {
        HttpHeaders headers = prepareHeadersWithBasicAuthorization();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        return new HttpEntity<>(new LinkedMultiValueMap<>(data), headers);
    }

    protected <T extends Ocs, E> T exchange(String url, HttpMethod method, HttpEntity<E> httpEntity, Class<T> clazz,
            Object... urlVariables) {
        return exchange(url, method, httpEntity, clazz, this::checkFailure, urlVariables);
    }

    protected <T extends Ocs, E> T exchange(String url, HttpMethod method, HttpEntity<E> httpEntity, Class<T> clazz,
            OwncloudResponseStatusChecker statusChecker, Object... urlVariables) {
        log.trace("Exchange Data by a {} Request with URL {}. Requested Class of returned Data is {}", method, url,
                clazz);
        ResponseEntity<T> response = restTemplate.exchange(url, method, httpEntity, clazz, urlVariables);
        T result = response.getBody();
        log.trace("Returned Meta-Data: {}", result.getMeta());
        log.debug("Check the returned Meta-Data for Errors");
        String authorizationUser = getAuthorizationUserFromHeaders(httpEntity.getHeaders());
        statusChecker.checkForFailure(authorizationUser, url, result.getMeta());
        return result;
    }

    protected String getAuthorizationUserFromHeaders(HttpHeaders headers) {
        Validate.notNull(headers);

        List<String> authorizations = headers.get(HttpHeaders.AUTHORIZATION);
        if (CollectionUtils.isEmpty(authorizations)) {
            return null;
        }

        String encodedCredentials = authorizations.get(0);
        if (StringUtils.startsWith(encodedCredentials, AUTHORIZATION_METHOD_PREFIX)) {
            encodedCredentials = StringUtils.substring(encodedCredentials, AUTHORIZATION_METHOD_PREFIX.length());
        }
        final byte[] rawDecodedCredentials = Base64.getDecoder().decode(encodedCredentials.getBytes());
        final String decodedCredentials = new String(rawDecodedCredentials);
        if (!StringUtils.contains(decodedCredentials, ':')) {
            return null;
        }
        return StringUtils.split(decodedCredentials, ':')[0];
    }

    protected void checkFailure(String username, String uri, Ocs.Meta meta) throws OwncloudStatusException {
        if ("ok".equals(meta.getStatus())) {
            return;
        }

        String exceptionMessage;
        switch (meta.getStatuscode()) {
        case 997:
            exceptionMessage = String.format("User %s is not authorized to access Resource %s", username, uri);
            log.warn("Error 997: {}", exceptionMessage);
            throw new AccessDeniedException(exceptionMessage);
        case 998:
            log.error("Error 998: {}", meta.getMessage());
            throw new UsernameNotFoundException(meta.getMessage());
        default:
            exceptionMessage = String.format("Unknown Error Code %d. Reason: %s", meta.getStatuscode(),
                    StringUtils.defaultIfEmpty(meta.getMessage(), ""));
            log.error(exceptionMessage);
            throw new IllegalStateException(exceptionMessage);
        }
    }

    protected OwncloudUserDetails convert(String username, Ocs.User user, Ocs.Groups groupsFromBackend) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        if (isAnyOwncloudGroupAvailable(groupsFromBackend)) {
            log.trace("Put {} Owncloud-Group(s) into the Authorities- and Group-List");
            groupsFromBackend.getData().getGroups().stream().map(Ocs.Groups.Data.Group::getGroup)
                    .map(SimpleGrantedAuthority::new).forEach(authorities::add);
        }

        log.debug("Convert User {} from {} to {}", username, user.getClass(), OwncloudUserDetails.class);
        return OwncloudUserDetails.builder().username(username).enabled(user.getData().isEnabled())
                .displayname(user.getData().getDisplayname()).email(user.getData().getEmail())
                .quota(user.getData().getQuota().getTotal()).authorities(authorities).build();
    }

    private boolean isAnyOwncloudGroupAvailable(Ocs.Groups groups) {
        return groups != null && groups.getData() != null && groups.getData().getGroups() != null;
    }
}