Skip to content

Commit

Permalink
dcache-xroot: store most recent login subject in door
Browse files Browse the repository at this point in the history
Motivation:

In the xrootd4j library, the authentication handling API entails
the return of the subject via an implementation-specific login.
The authorization API, however, is based upon the check of permissions
on a specific path, and does not provide for side-effects to
the subject.

This has been complicated, however, by the SciTokens authorization protocol,
at least in its dCache manifestation, because the bearer token may actually
contain new information about the subject that was not present at login, as
well as the specific restrictions now granted overriding the preceding
restrictions established at login.

The original solution was to stack login information in the door such that
a request accesses the most recent restrictions.  What was not done, however,
was to update in a similar manner the actual
login subject.

This needs to happen because the xrootd specific session info, which contains
the original login Subject, is not updated during authorization. Using the
xrootd4j session Subject in the case of GSI, for instance, works fine, but using it
with SciTokens will not work unless the ZTN protocol is active at login AND
the ZTN token is identical to the one used on the path URL, which very likely
may not be the case.

One could try to fix this in the xrootd4j library by adjusting the authorization
handler API to be able to update the session subject, but since dCache will continue
to require the login info stack in the door to get the correct, dCache-specific
Restriction object, it makes more sense simply not to use the xrootd4j session
Subject at all, and instead store the most recent Subject with the Restriction
in the door.

Modification:

Rename SessionInfo to LoginSessionInfo for clarity, and add the Subject to it.
Always access this Subject in the door, and ignore the one contained in the
request object's xrootd Session.

Result:

Correct subject is used to access the path for both GSI and SciToken protocols
(and hopefully any future ones).

Target: master
Request: 7.0
Request: 6.2
Patch: https://rb.dcache.org/r/12988/
Acked-by: Tigran
  • Loading branch information
alrossi committed Apr 19, 2021
1 parent 47896d7 commit e2f64d2
Showing 1 changed file with 84 additions and 59 deletions.
Expand Up @@ -60,7 +60,7 @@
import org.dcache.xrootd.core.XrootdException;
import org.dcache.xrootd.core.XrootdSession;
import org.dcache.xrootd.protocol.XrootdProtocol;
import org.dcache.xrootd.protocol.XrootdProtocol.FilePerm;
import org.dcache.xrootd.protocol.XrootdProtocol.*;
import org.dcache.xrootd.protocol.messages.AwaitAsyncResponse;
import org.dcache.xrootd.protocol.messages.CloseRequest;
import org.dcache.xrootd.protocol.messages.DirListRequest;
Expand Down Expand Up @@ -103,23 +103,26 @@ public class XrootdRedirectHandler extends ConcurrentXrootdRequestHandler
private static final Logger _log =
LoggerFactory.getLogger(XrootdRedirectHandler.class);

private class SessionInfo
private class LoginSessionInfo
{
private final Subject subject;
private final Restriction restriction;
private final OptionalLong maximumUploadSize;
private final FsPath userRootPath;
private final boolean loggedIn;

SessionInfo(Restriction restriction)
LoginSessionInfo(Restriction restriction)
{
subject = new Subject();
this.restriction = restriction;
maximumUploadSize = OptionalLong.empty();
userRootPath = null;
loggedIn = false;
}

SessionInfo(LoginReply reply)
LoginSessionInfo(LoginReply reply)
{
subject = reply.getSubject();
restriction = reply.getRestriction();
userRootPath = reply.getLoginAttributes().stream()
.filter(RootDirectory.class::isInstance)
Expand All @@ -137,6 +140,11 @@ public Restriction getRestriction()
return restriction;
}

public Subject getSubject()
{
return subject;
}

public OptionalLong getMaximumUploadSize()
{
return maximumUploadSize;
Expand All @@ -155,8 +163,8 @@ public boolean isLoggedIn()

private final XrootdDoor _door;
private final Map<String, String> _appIoQueues;
private final SessionInfo _defaultSessionInfo;
private final Deque<SessionInfo> _logins;
private final LoginSessionInfo _defaultLoginSessionInfo;
private final Deque<LoginSessionInfo> _logins;
private final FsPath _rootPath;

/**
Expand All @@ -173,7 +181,7 @@ public XrootdRedirectHandler(XrootdDoor door, FsPath rootPath, ExecutorService e
_rootPath = rootPath;
_queryConfig = queryConfig;
_appIoQueues = appIoQueues;
_defaultSessionInfo = new SessionInfo(Restrictions.denyAll());
_defaultLoginSessionInfo = new LoginSessionInfo(Restrictions.denyAll());
_logins = new ArrayDeque<>(2);
}

Expand Down Expand Up @@ -256,7 +264,7 @@ protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRe

InetSocketAddress localAddress = getDestinationAddress();
InetSocketAddress remoteAddress = getSourceAddress();
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();

Map<String,String> opaque;

Expand All @@ -279,7 +287,7 @@ protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRe

XrootdResponse response
= conditionallyHandleThirdPartyRequest(req,
sessionInfo,
loginSessionInfo,
opaque,
path,
remoteAddress.getHostName());
Expand Down Expand Up @@ -336,11 +344,13 @@ protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRe
// TODO: replace with req.isPersistOnSuccessfulClose() with the latest xrootd4j
transfer = _door.write(remoteAddress, path, triedHosts,
ioQueue, uuid, true, overwrite, size,
sessionInfo.getMaximumUploadSize(),
localAddress, req.getSubject(), sessionInfo.getRestriction(),
loginSessionInfo.getMaximumUploadSize(),
localAddress,
loginSessionInfo.getSubject(),
loginSessionInfo.getRestriction(),
persistOnSuccessfulClose,
((sessionInfo.isLoggedIn()) ?
sessionInfo.getUserRootPath() : _rootPath),
((loginSessionInfo.isLoggedIn()) ?
loginSessionInfo.getUserRootPath() : _rootPath),
req.getSession().getDelegatedCredential(),
opaque);
} else {
Expand All @@ -357,14 +367,14 @@ protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRe
Subject subject;

if (opaque.get("tpc.key") == null) {
subject = req.getSubject();
subject = loginSessionInfo.getSubject();
} else {
subject = Subjects.ROOT;
}

transfer = _door.read(remoteAddress, path, triedHosts, ioQueue,
uuid, localAddress, subject,
sessionInfo.getRestriction(), opaque);
loginSessionInfo.getRestriction(), opaque);

/*
* Again, if this is a tpc transfer, then dCache is source here.
Expand Down Expand Up @@ -459,7 +469,7 @@ protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRe
*/
private XrootdResponse<OpenRequest>
conditionallyHandleThirdPartyRequest(OpenRequest req,
SessionInfo sessionInfo,
LoginSessionInfo loginSessionInfo,
Map<String,String> opaque,
FsPath fsPath,
String remoteHost)
Expand All @@ -470,10 +480,13 @@ protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRe
"Read permission denied");
}

Subject subject = loginSessionInfo.getSubject();
Restriction restriction = loginSessionInfo.getRestriction();

if ("placement".equals(opaque.get("tpc.stage"))) {
FileStatus status = _door.getFileStatus(fsPath,
req.getSubject(),
sessionInfo.getRestriction(),
subject,
restriction,
remoteHost);
int fd = _door.nextTpcPlaceholder();
_log.debug("placement response to {} sent to {} with fhandle {}.",
Expand Down Expand Up @@ -581,8 +594,8 @@ protected XrootdResponse<OpenRequest> doOnOpen(ChannelHandlerContext ctx, OpenRe
_log.debug("Open request {} from client to door as source, "
+ "info {}: OK.", req, info);
FileStatus status = _door.getFileStatus(fsPath,
req.getSubject(),
sessionInfo.getRestriction(),
subject,
restriction,
remoteHost);
int flags = status.getFlags();

Expand Down Expand Up @@ -721,12 +734,14 @@ protected XrootdResponse<CloseRequest> doOnClose(ChannelHandlerContext ctx, Clos
protected XrootdResponse<StatRequest> doOnStat(ChannelHandlerContext ctx, StatRequest req)
throws XrootdException
{
String path = req.getPath();
try {
String path = req.getPath();
LoginSessionInfo loginSessionInfo = sessionInfo();
InetSocketAddress client = getSourceAddress();
SessionInfo sessionInfo = sessionInfo();
return new StatResponse(req, _door.getFileStatus(createFullPath(path), req.getSubject(),
sessionInfo.getRestriction(),

return new StatResponse(req, _door.getFileStatus(createFullPath(path),
loginSessionInfo.getSubject(),
loginSessionInfo.getRestriction(),
client.getAddress().getHostAddress()));
} catch (FileNotFoundCacheException e) {
throw xrootdException(e.getRc(), "No such file");
Expand All @@ -753,11 +768,13 @@ protected XrootdResponse<StatxRequest> doOnStatx(ChannelHandlerContext ctx, Stat
for (int i = 0; i < paths.length; i++) {
paths[i] = createFullPath(req.getPaths()[i]);
}
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();
Subject subject = loginSessionInfo.getSubject();
Restriction restriction = loginSessionInfo.getRestriction();
return new StatxResponse(req,
_door.getMultipleFileStatuses(paths,
req.getSubject(),
sessionInfo.getRestriction()));
subject,
restriction));
} catch (TimeoutCacheException e) {
throw xrootdException(e.getRc(), "Internal timeout");
} catch (PermissionDeniedCacheException e) {
Expand All @@ -781,10 +798,10 @@ protected XrootdResponse<RmRequest> doOnRm(ChannelHandlerContext ctx, RmRequest
_log.info("Trying to delete {}", req.getPath());

try {
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();
_door.deleteFile(createFullPath(req.getPath()),
req.getSubject(),
sessionInfo.getRestriction());
loginSessionInfo.getSubject(),
loginSessionInfo.getRestriction());
return withOk(req);
} catch (TimeoutCacheException e) {
throw xrootdException(e.getRc(), "Internal timeout");
Expand All @@ -810,10 +827,10 @@ protected XrootdResponse<RmDirRequest> doOnRmDir(ChannelHandlerContext ctx, RmDi
_log.info("Trying to delete directory {}", req.getPath());

try {
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();
_door.deleteDirectory(createFullPath(req.getPath()),
req.getSubject(),
sessionInfo.getRestriction());
loginSessionInfo.getSubject(),
loginSessionInfo.getRestriction());
return withOk(req);
} catch (TimeoutCacheException e) {
throw xrootdException(e.getRc(), "Internal timeout");
Expand All @@ -838,11 +855,11 @@ protected XrootdResponse<MkDirRequest> doOnMkDir(ChannelHandlerContext ctx, MkDi
_log.info("Trying to create directory {}", req.getPath());

try {
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();
_door.createDirectory(createFullPath(req.getPath()),
req.shouldMkPath(),
req.getSubject(),
sessionInfo.getRestriction());
loginSessionInfo.getSubject(),
loginSessionInfo.getRestriction());
return withOk(req);
} catch (TimeoutCacheException e) {
throw xrootdException(e.getRc(), "Internal timeout");
Expand Down Expand Up @@ -874,11 +891,11 @@ protected XrootdResponse<MvRequest> doOnMv(ChannelHandlerContext ctx, MvRequest
_log.info("Trying to rename {} to {}", req.getSourcePath(), req.getTargetPath());

try {
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();
_door.moveFile(createFullPath(req.getSourcePath()),
createFullPath(req.getTargetPath()),
req.getSubject(),
sessionInfo.getRestriction());
loginSessionInfo.getSubject(),
loginSessionInfo.getRestriction());
return withOk(req);
} catch (TimeoutCacheException e) {
throw xrootdException(e.getRc(), "Internal timeout");
Expand Down Expand Up @@ -941,10 +958,10 @@ protected XrootdResponse<QueryRequest> doOnQuery(ChannelHandlerContext ctx, Quer
try {
ChecksumInfo checksumInfo = new ChecksumInfo(msg.getPath(),
msg.getOpaque());
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();
Set<Checksum> checksums = _door.getChecksums(createFullPath(msg.getPath()),
msg.getSubject(),
sessionInfo.getRestriction());
loginSessionInfo.getSubject(),
loginSessionInfo.getRestriction());
return selectChecksum(checksumInfo, checksums, msg);
} catch (CacheException e) {
throw xrootdException(e);
Expand All @@ -970,15 +987,15 @@ protected XrootdResponse<DirListRequest> doOnDirList(ChannelHandlerContext ctx,
if (!_door.isReadAllowed(fullListPath)) {
throw new PermissionDeniedCacheException("Permission denied.");
}
SessionInfo sessionInfo = sessionInfo();
LoginSessionInfo loginSessionInfo = sessionInfo();
Subject subject = loginSessionInfo.getSubject();
Restriction restriction = loginSessionInfo.getRestriction();
if (request.isDirectoryStat()) {
_door.listPath(fullListPath, request.getSubject(),
sessionInfo.getRestriction(),
new StatListCallback(request, fullListPath, ctx),
_door.listPath(fullListPath, subject, restriction,
new StatListCallback(request, subject, restriction, fullListPath, ctx),
_door.getRequiredAttributesForFileStatus());
} else {
_door.listPath(fullListPath, request.getSubject(),
sessionInfo.getRestriction(),
_door.listPath(fullListPath, subject, restriction,
new ListCallback(request, ctx),
EnumSet.noneOf(FileAttribute.class));
}
Expand Down Expand Up @@ -1200,23 +1217,30 @@ public void timeout(String error) {

private class StatListCallback extends ListCallback
{
private final String _client;
protected final FsPath _dirPath;

public StatListCallback(DirListRequest request, FsPath dirPath, ChannelHandlerContext context)
private final String _client;
private final Subject _subject;
private final Restriction _restriction;

public StatListCallback(DirListRequest request,
Subject subject,
Restriction restriction,
FsPath dirPath,
ChannelHandlerContext context)
{
super(request, context);
_client = getSourceAddress().getAddress().getHostAddress();
_dirPath = dirPath;
_subject = subject;
_restriction = restriction;
}

@Override
public void success(PnfsListDirectoryMessage message)
{
SessionInfo sessionInfo = sessionInfo();
message.getEntries().stream().forEach(
e -> _response.add(e.getName(), _door.getFileStatus(_request.getSubject(),
sessionInfo.getRestriction(),
e -> _response.add(e.getName(), _door.getFileStatus(_subject,
_restriction,
_dirPath.child(e.getName()),
_client, e.getFileAttributes())));
if (message.isFinal()) {
Expand All @@ -1237,9 +1261,9 @@ private void loggedIn(LoginEvent event) {
}

LoginReply reply = event.getLoginReply();
SessionInfo info = reply == null
? new SessionInfo(Restrictions.none())
: new SessionInfo(reply);
LoginSessionInfo info = reply == null
? new LoginSessionInfo(Restrictions.none())
: new LoginSessionInfo(reply);

_logins.push(info);
}
Expand All @@ -1264,16 +1288,17 @@ private FsPath createFullPath(String path)
*
* @return current info.
*/
private SessionInfo sessionInfo()
private LoginSessionInfo sessionInfo()
{
if (_logins.size() > 1) {
return _logins.pop();
}

if (_logins.isEmpty()) {
return _defaultSessionInfo;
return _defaultLoginSessionInfo;
}

return _logins.peek();
}

}

0 comments on commit e2f64d2

Please sign in to comment.