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

io.milton.http.webdav.PropPatchHandler Maven / Gradle / Ivy

/*
 * 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 io.milton.http.webdav;

import io.milton.common.ReadingException;
import io.milton.common.WritingException;
import io.milton.event.PropPatchEvent;
import io.milton.http.AuthenticationService.AuthStatus;
import io.milton.http.ExistingEntityHandler;
import io.milton.http.HttpManager;
import io.milton.http.Request;
import io.milton.http.Request.Method;
import io.milton.http.ResourceHandlerHelper;
import io.milton.http.Response;
import io.milton.http.Response.Status;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.ConflictException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.property.PropertyAuthoriser;
import io.milton.property.PropertyHandler;
import io.milton.resource.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.xml.namespace.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PropPatchHandler implements ExistingEntityHandler, PropertyHandler {

    private final static Logger log = LoggerFactory.getLogger( PropPatchHandler.class );
    private final ResourceHandlerHelper resourceHandlerHelper;
    private final PropPatchRequestParser requestParser;
    private final PropPatchSetter patchSetter;
    private final WebDavResponseHandler responseHandler;
    private final PropertyAuthoriser permissionService;

    public PropPatchHandler( ResourceHandlerHelper resourceHandlerHelper, PropPatchRequestParser requestParser, PropPatchSetter patchSetter, WebDavResponseHandler responseHandler, PropertyAuthoriser permissionService ) {
        this.resourceHandlerHelper = resourceHandlerHelper;
        this.requestParser = requestParser;
        this.patchSetter = patchSetter;
        this.responseHandler = responseHandler;
		this.permissionService = permissionService;
    }

	@Override
    public String[] getMethods() {
        return new String[]{Method.PROPPATCH.code};
    }

	@Override
    public boolean isCompatible( Resource r ) {
        return patchSetter.supports( r );
    }

	@Override
    public void process( HttpManager httpManager, Request request, Response response ) throws ConflictException, NotAuthorizedException, BadRequestException {
        resourceHandlerHelper.process( httpManager, request, response, this );
    }

	@Override
    public void processResource( HttpManager manager, Request request, Response response, Resource resource ) throws NotAuthorizedException, ConflictException, BadRequestException {
        long t = System.currentTimeMillis();
        try {

            manager.onProcessResourceStart( request, response, resource );

            if( resourceHandlerHelper.isNotCompatible( resource, request.getMethod() ) || !isCompatible( resource ) ) {
                log.debug( "resource not compatible. Resource class: " + resource.getClass() + " handler: " + getClass() );
                responseHandler.respondMethodNotImplemented( resource, response, request );
                return;
            }

            AuthStatus authStatus = resourceHandlerHelper.checkAuthentication( manager, resource, request );
            if( authStatus != null && authStatus.loginFailed ) {
                log.debug( "authentication failed. respond with: " + responseHandler.getClass().getCanonicalName() + " resource: " + resource.getClass().getCanonicalName() );
                responseHandler.respondUnauthorised( resource, response, request );
                return;
            }

            if( request.getMethod().isWrite ) {
                if( resourceHandlerHelper.isLockedOut( request, resource ) ) {
                    response.setStatus( Status.SC_LOCKED ); // replace with responsehandler method
                    return;
                }
            }

            processExistingResource( manager, request, response, resource );
        } finally {
            t = System.currentTimeMillis() - t;
            manager.onProcessResourceFinish( request, response, resource, t );
        }
    }

	@Override
    public void processExistingResource( HttpManager manager, Request request, Response response, Resource resource ) throws NotAuthorizedException, BadRequestException, ConflictException {
        // todo: check if token header
        try {
            PropFindResponse resp = doPropPatch( request, resource);

            manager.getEventManager().fireEvent( new PropPatchEvent( resource, resp ) );
            List responses = new ArrayList<>();
            responses.add( resp );
            responseHandler.respondPropFind( responses, response, request, resource );
        } catch( NotAuthorizedException e ) {
            responseHandler.respondUnauthorised( resource, response, request );
        } catch( ReadingException ex ) {
            throw new RuntimeException( ex );
        } catch( IOException ex ) {
            throw new RuntimeException( ex );
        }
    }

	public PropFindResponse doPropPatch(Request request, Resource resource) throws NotAuthorizedException, IOException, BadRequestException, ConflictException {
		return doPropPatch(request, resource, patchSetter);
	}
		
    public PropFindResponse doPropPatch(Request request, Resource resource, PropPatchSetter propPatchSetter) throws NotAuthorizedException, IOException, BadRequestException, ConflictException {
        InputStream in = request.getInputStream();
        PropPatchParseResult parseResult = requestParser.getRequestedFields(in);
        // Check that the current user has permission to write requested fields
        Set allFields = getAllFields(parseResult);
        if (log.isTraceEnabled()) {
            log.trace("check permissions with: " + permissionService.getClass().getCanonicalName());
        }
        Set errorFields = permissionService.checkPermissions(request, request.getMethod(), PropertyAuthoriser.PropertyPermission.WRITE, allFields, resource);
        if (errorFields != null && errorFields.size() > 0) {
            throw new NotAuthorizedException(resource);
        }
        String href = request.getAbsoluteUrl();
		href = DefaultPropFindPropertyBuilder.fixUrlForWindows(href);
        return propPatchSetter.setProperties(href, parseResult, resource);
    }

    private Set getAllFields( PropPatchParseResult parseResult ) {
        Set set = new HashSet<>();
        if( parseResult.getFieldsToRemove() != null ) {
            set.addAll( parseResult.getFieldsToRemove() );
        }
        if( parseResult.getFieldsToSet() != null ) {
            set.addAll( parseResult.getFieldsToSet().keySet() );
        }
        return set;
    }

	@Override
    public PropertyAuthoriser getPermissionService() {
        return permissionService;
    }

    public static class Field {

        public final String name;
        String namespaceUri;

        public Field( String name ) {
            this.name = name;
        }

        public void setNamespaceUri( String namespaceUri ) {
            this.namespaceUri = namespaceUri;
        }

        public String getNamespaceUri() {
            return namespaceUri;
        }
    }

    public static class SetField extends Field {

        public final String value;

        public SetField( String name, String value ) {
            super( name );
            this.value = value;
        }
    }

    public static class Fields implements Iterable {

        /**
         * fields to remove
         */
        public final List removeFields = new ArrayList<>();
        /**
         * fields to set to a value
         */
        public final List setFields = new ArrayList<>();

        private int size() {
            return removeFields.size() + setFields.size();
        }

		@Override
        public Iterator iterator() {
            List list = new ArrayList<>(removeFields);
            list.addAll( setFields );
            return list.iterator();
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy