org.dcache.xrootd.core.XrootdAuthorizationHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.xrootd.core.XrootdAuthorizationHandler.java

Source

/**
 * Copyright (C) 2011-2018 dCache.org <support@dcache.org>
 *
 * This file is part of xrootd4j.
 *
 * xrootd4j is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * xrootd4j 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with xrootd4j.  If not, see http://www.gnu.org/licenses/.
 */
package org.dcache.xrootd.core;

import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;

import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;

import org.dcache.xrootd.plugins.AuthorizationFactory;
import org.dcache.xrootd.plugins.AuthorizationHandler;
import org.dcache.xrootd.protocol.messages.CloseRequest;
import org.dcache.xrootd.protocol.messages.DirListRequest;
import org.dcache.xrootd.protocol.messages.LocateRequest;
import org.dcache.xrootd.protocol.messages.MkDirRequest;
import org.dcache.xrootd.protocol.messages.MvRequest;
import org.dcache.xrootd.protocol.messages.OpenRequest;
import org.dcache.xrootd.protocol.messages.PathRequest;
import org.dcache.xrootd.protocol.messages.PrepareRequest;
import org.dcache.xrootd.protocol.messages.ProtocolRequest;
import org.dcache.xrootd.protocol.messages.QueryRequest;
import org.dcache.xrootd.protocol.messages.ReadRequest;
import org.dcache.xrootd.protocol.messages.ReadVRequest;
import org.dcache.xrootd.protocol.messages.RmDirRequest;
import org.dcache.xrootd.protocol.messages.RmRequest;
import org.dcache.xrootd.protocol.messages.SetRequest;
import org.dcache.xrootd.protocol.messages.StatRequest;
import org.dcache.xrootd.protocol.messages.StatxRequest;
import org.dcache.xrootd.protocol.messages.SyncRequest;
import org.dcache.xrootd.protocol.messages.WriteRequest;
import org.dcache.xrootd.protocol.messages.XrootdRequest;
import org.dcache.xrootd.util.OpaqueStringParser;
import org.dcache.xrootd.util.ParseException;

import static org.dcache.xrootd.protocol.XrootdProtocol.*;

@Sharable
public class XrootdAuthorizationHandler extends XrootdRequestHandler {
    private final AuthorizationFactory _authorizationFactory;

    public XrootdAuthorizationHandler(AuthorizationFactory authorizationFactory) {
        _authorizationFactory = authorizationFactory;
    }

    @Override
    protected Void doOnStat(ChannelHandlerContext ctx, StatRequest req) throws XrootdException {
        authorize(ctx, req, FilePerm.READ);
        ctx.fireChannelRead(req);
        return null;
    }

    @Override
    protected Void doOnStatx(ChannelHandlerContext ctx, StatxRequest req) throws XrootdException {
        if (req.getPaths().length == 0) {
            throw new XrootdException(kXR_ArgMissing, "no paths specified");
        }

        String[] paths = req.getPaths();
        String[] opaques = req.getOpaques();
        for (int i = 0; i < paths.length; i++) {
            paths[i] = authorize(ctx, req, FilePerm.READ, paths[i], opaques[i]);
        }
        req.setPaths(paths);

        ctx.fireChannelRead(req);
        return null;
    }

    @Override
    protected Void doOnRm(ChannelHandlerContext ctx, RmRequest req) throws XrootdException {
        if (req.getPath().isEmpty()) {
            throw new XrootdException(kXR_ArgMissing, "no path specified");
        }
        authorize(ctx, req, FilePerm.DELETE);
        ctx.fireChannelRead(req);
        return null;
    }

    @Override
    protected Void doOnRmDir(ChannelHandlerContext ctx, RmDirRequest req) throws XrootdException {
        if (req.getPath().isEmpty()) {
            throw new XrootdException(kXR_ArgMissing, "no path specified");
        }

        authorize(ctx, req, FilePerm.DELETE);
        ctx.fireChannelRead(req);
        return null;
    }

    @Override
    protected Void doOnMkDir(ChannelHandlerContext ctx, MkDirRequest req) throws XrootdException {
        if (req.getPath().isEmpty()) {
            throw new XrootdException(kXR_ArgMissing, "no path specified");
        }

        authorize(ctx, req, FilePerm.WRITE);
        ctx.fireChannelRead(req);
        return null;
    }

    @Override
    protected Void doOnMv(ChannelHandlerContext ctx, MvRequest req) throws XrootdException {
        String sourcePath = req.getSourcePath();
        if (sourcePath.isEmpty()) {
            throw new XrootdException(kXR_ArgMissing, "No source path specified");
        }

        String targetPath = req.getTargetPath();
        if (targetPath.isEmpty()) {
            throw new XrootdException(kXR_ArgMissing, "No target path specified");
        }

        req.setSourcePath(authorize(ctx, req, FilePerm.DELETE, req.getSourcePath(), req.getSourceOpaque()));
        req.setTargetPath(authorize(ctx, req, FilePerm.WRITE, req.getTargetPath(), req.getTargetOpaque()));
        ctx.fireChannelRead(req);
        return null;
    }

    @Override
    protected Void doOnDirList(ChannelHandlerContext ctx, DirListRequest request) throws XrootdException {
        String path = request.getPath();
        if (path.isEmpty()) {
            throw new XrootdException(kXR_ArgMissing, "no source path specified");
        }
        authorize(ctx, request, FilePerm.READ);
        ctx.fireChannelRead(request);
        return null;
    }

    @Override
    protected Void doOnPrepare(ChannelHandlerContext ctx, PrepareRequest msg) {
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnLocate(ChannelHandlerContext ctx, LocateRequest msg) throws XrootdException {
        String path = msg.getPath();
        if (!path.startsWith("*")) {
            path = authorize(ctx, msg, FilePerm.READ, path, msg.getOpaque());
        } else if (!path.equals("*")) {
            path = authorize(ctx, msg, FilePerm.READ, path.substring(1), msg.getOpaque());
        }
        msg.setPath(path);
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnOpen(ChannelHandlerContext ctx, OpenRequest msg) throws XrootdException {
        authorize(ctx, msg, msg.getRequiredPermission());
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnRead(ChannelHandlerContext ctx, ReadRequest msg) throws XrootdException {
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnReadV(ChannelHandlerContext ctx, ReadVRequest msg) throws XrootdException {
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnWrite(ChannelHandlerContext ctx, WriteRequest msg) throws XrootdException {
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnSync(ChannelHandlerContext ctx, SyncRequest msg) throws XrootdException {
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnClose(ChannelHandlerContext ctx, CloseRequest msg) throws XrootdException {
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnProtocolRequest(ChannelHandlerContext ctx, ProtocolRequest msg) throws XrootdException {
        ctx.fireChannelRead(msg);
        return null;
    }

    @Override
    protected Void doOnQuery(ChannelHandlerContext ctx, QueryRequest req) throws XrootdException {
        switch (req.getReqcode()) {
        case kXR_Qcksum:
        case kXR_Qxattr:
            String args = req.getArgs();
            int pos = args.indexOf(OPAQUE_DELIMITER);
            String path;
            String opaque;
            if (pos > -1) {
                path = args.substring(0, pos);
                opaque = args.substring(pos + 1);
            } else {
                path = args;
                opaque = "";
            }
            req.setArgs(authorize(ctx, req, FilePerm.READ, path, opaque));
            break;
        }
        ctx.fireChannelRead(req);
        return null;
    }

    @Override
    protected Void doOnSet(ChannelHandlerContext ctx, SetRequest request) throws XrootdException {
        ctx.fireChannelRead(request);
        return null;
    }

    private void authorize(ChannelHandlerContext ctx, PathRequest request, FilePerm neededPerm)
            throws XrootdException {
        request.setPath(authorize(ctx, request, neededPerm, request.getPath(), request.getOpaque()));
    }

    /**
     * Performs authorization check and path mapping.
     *
     * @param ctx The ChannelHandlerContext
     * @param request The xrootd message
     * @param neededPerm The permission level that is required for the operation
     * @param path The path to which access is requested
     * @param opaque Opaque data sent with the request
     * @return The path to which access is granted.
     * @throws XrootdException if the request is denied
     */
    private String authorize(ChannelHandlerContext ctx, XrootdRequest request, FilePerm neededPerm, String path,
            String opaque) throws XrootdException {
        try {
            InetSocketAddress destinationAddress = getDestinationAddress();
            InetSocketAddress sourceAddress = getSourceAddress();

            AuthorizationHandler handler = _authorizationFactory.createHandler();
            return handler.authorize(request.getSubject(), destinationAddress, sourceAddress, path,
                    OpaqueStringParser.getOpaqueMap(opaque), request.getRequestId(), neededPerm);
        } catch (GeneralSecurityException e) {
            throw new XrootdException(kXR_ServerError, "Authorization check failed: " + e.getMessage());
        } catch (SecurityException e) {
            throw new XrootdException(kXR_NotAuthorized, "Permission denied: " + e.getMessage());
        } catch (ParseException e) {
            throw new XrootdException(kXR_NotAuthorized,
                    "Invalid opaque data: " + e.getMessage() + " (opaque=" + opaque + ")");
        }
    }
}