Skip to content

Commit

Permalink
PoolManager : stage protection add protocol to the list of discrimina…
Browse files Browse the repository at this point in the history
…tors

Motivation:

Ability to restrict staging to specific protocol was missing in
stage protection specification. Additionally it was not possible to
specify DN, FQAN, storage group and protocol that are not allowed
to stage.

Modification:

Added protocol to the list of discriminators in stage protection
configuration. Added ability to negate match on DN, FQAN, storage group
or protocol effectively implementing blacklisting.

Result:

Stage protection configuration can be used as a blacklist. Protocol is
added to the list of discriminators.

Target: master
Request: 3.x
Request: 2.16
Requires-notes: yes
Requires-book: yes
Patch: https://rb.dcache.org/r/9966/
Acked-by: Paul Millar

Conflicts:
	modules/dcache/src/main/java/diskCacheV111/util/CheckStagePermission.java
	modules/dcache/src/test/java/diskCacheV111/util/CheckStagePermissionTests.java
  • Loading branch information
DmitryLitvintsev committed Jan 13, 2017
1 parent 3b8520d commit df629f2
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1969,7 +1969,9 @@ public void fileAttributesAvailable()
EnumSet<RequestContainerV5.RequestState> allowedStates;
try {
allowedStates =
_settings.getCheckStagePermission().canPerformStaging(_subject, _fileAttributes)
_settings.getCheckStagePermission().canPerformStaging(_subject,
_fileAttributes,
_protocolInfo)
? RequestContainerV5.allStates
: RequestContainerV5.allStatesExceptStage;
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,9 @@ private boolean canStage()
try {
PoolMgrSelectReadPoolMsg msg =
(PoolMgrSelectReadPoolMsg) envelope.getMessageObject();
if (_stagePolicyDecisionPoint.canPerformStaging(msg.getSubject(), msg.getFileAttributes())) {
if (_stagePolicyDecisionPoint.canPerformStaging(msg.getSubject(),
msg.getFileAttributes(),
msg.getProtocolInfo())) {
return true;
}
} catch (IOException | PatternSyntaxException e) {
Expand Down
285 changes: 148 additions & 137 deletions modules/dcache/src/main/java/diskCacheV111/util/CheckStagePermission.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import diskCacheV111.vehicles.ProtocolInfo;

import org.dcache.auth.FQAN;
import org.dcache.auth.Subjects;
import org.dcache.vehicles.FileAttributes;

public class CheckStagePermission {
private static final Pattern LINE_PATTERN = Pattern.compile("\"(?<dn>[^\"]*)\"([ \t]+\"(?<fqan>[^\"]*)\"([ \t]+\"(?<su>[^\"]*)\"([ \t]+\"(?<protocol>[^\"]*)\")?)?)?");
private File _stageConfigFile;
private long _lastTimeReadingStageConfigFile;
private List<Pattern[]> _regexList;
Expand Down Expand Up @@ -50,7 +53,9 @@ public CheckStagePermission(String stageConfigurationFilePath) {
* @return true if and only if the subject is allowed to perform
* staging
*/
public boolean canPerformStaging(Subject subject, FileAttributes fileAttributes)
public boolean canPerformStaging(Subject subject,
FileAttributes fileAttributes,
ProtocolInfo protocolInfo)
throws PatternSyntaxException, IOException
{
if (!_isEnabled || Subjects.isRoot(subject)) {
Expand All @@ -73,11 +78,13 @@ public boolean canPerformStaging(Subject subject, FileAttributes fileAttributes)
dn = "";
}

String protocol = protocolInfo.getProtocol()+"/"+protocolInfo.getMajorVersion();

if (fqans.isEmpty()) {
return canPerformStaging(dn, null, storeUnit);
return canPerformStaging(dn, null, storeUnit, protocol);
} else {
for (FQAN fqan: fqans) {
if (canPerformStaging(dn, fqan, storeUnit)) {
if (canPerformStaging(dn, fqan, storeUnit, protocol)) {
return true;
}
}
Expand All @@ -99,145 +106,149 @@ public boolean canPerformStaging(Subject subject, FileAttributes fileAttributes)
* @throws PatternSyntaxException
* @throws IOException
*/
public boolean canPerformStaging(String dn, FQAN fqan, String storeUnit) throws PatternSyntaxException, IOException {

if ( !_isEnabled ) {
return true;
}

if ( !_stageConfigFile.exists() ) {
//if file does not exist, staging is denied for all users
return false;
}

if ( fileNeedsRereading() ) {
rereadConfig();
}

return userMatchesPredicates(dn, Objects.toString(fqan, ""), storeUnit);
}

/**
* Reread the contents of the configuration file.
* @throws IOException
* @throws PatternSyntaxException
*/
void rereadConfig() throws PatternSyntaxException, IOException {
_fileWriteLock.lock();
try {
if ( fileNeedsRereading() ) {
try (BufferedReader reader = new BufferedReader(new FileReader(_stageConfigFile))) {
_regexList = readStageConfigFile(reader);
_lastTimeReadingStageConfigFile = System
.currentTimeMillis();
}

}
} finally {
_fileWriteLock.unlock();
}
}

/**
* Check whether the stageConfigFile needs rereading.
*
* @return true if the file should be reread.
*/
boolean fileNeedsRereading() {
long modificationTimeStageConfigFile;
modificationTimeStageConfigFile = _stageConfigFile.lastModified();

return modificationTimeStageConfigFile > _lastTimeReadingStageConfigFile;
}

/**
* Check whether the user matches predicates, that is, whether the user is in the
* list of authorized users that are allowed to perform staging of the object in the
* given storage group.
*
* @param dn user's Distinguished Name
* @param fqanStr user's FQAN as a String
* @param storeUnit object's storage unit
* @return true if the user and object match predicates
*/
boolean userMatchesPredicates(String dn, String fqanStr, String storeUnit) {
_fileReadLock.lock();
try {
for (Pattern[] regexLine : _regexList) {
if ( regexLine[0].matcher(dn).matches() ) {

if ( regexLine[1] == null ) {
return true; // line contains only DN; DN match -> STAGE allowed
} else if ( regexLine[1].matcher(fqanStr).matches() && (regexLine[2] == null || regexLine[2].matcher(storeUnit).matches()) ) {
return true;
//two cases covered here:
//line contains DN and FQAN; DN and FQAN match -> STAGE allowed
//line contains DN, FQAN, storeUnit; DN, FQAN, storeUnit match -> STAGE allowed
}
}
}
} finally {
_fileReadLock.unlock();
}
return false;
}

/**
* Read configuration file and create list of compiled patterns, containing DNs and FQANs(optionally)
* of the users that are allowed to perform staging,
* as well as storage group of the object to be staged (optionally).
*
* @param reader
* @return list of compiled patterns
* @throws IOException
* @throws PatternSyntaxException
*/
List<Pattern[]> readStageConfigFile(BufferedReader reader) throws IOException, PatternSyntaxException {

String line;
Pattern linePattern = Pattern.compile("\"([^\"]*)\"([ \t]+\"([^\"]*)\"([ \t]+\"([^\"]*)\")?)?");
Matcher matcherLine;

List<Pattern[]> regexList = new ArrayList<>();

while ((line = reader.readLine()) != null) {

line = line.trim();
matcherLine = linePattern.matcher(line);
if ( line.startsWith("#") || line.isEmpty() ) { //commented or empty line
continue;
}

if ( !matcherLine.matches() ) {
continue;
}
public boolean canPerformStaging(String dn,
FQAN fqan,
String storeUnit,
String protocol) throws PatternSyntaxException, IOException {

if ( !_isEnabled ) {
return true;
}

if ( !_stageConfigFile.exists() ) {
//if file does not exist, staging is denied for all users
return false;
}

if ( fileNeedsRereading() ) {
rereadConfig();
}

return userMatchesPredicates(dn,
Objects.toString(fqan, ""),
storeUnit,
protocol);
}

/**
* Reread the contents of the configuration file.
* @throws IOException
* @throws PatternSyntaxException
*/
private void rereadConfig() throws PatternSyntaxException, IOException {
_fileWriteLock.lock();
try {
if ( fileNeedsRereading() ) {
try (BufferedReader reader = new BufferedReader(new FileReader(_stageConfigFile))) {
_regexList = readStageConfigFile(reader);
_lastTimeReadingStageConfigFile = System
.currentTimeMillis();
}

}
} finally {
_fileWriteLock.unlock();
}
}

/**
* Check whether the stageConfigFile needs rereading.
*
* @return true if the file should be reread.
*/
private boolean fileNeedsRereading() {
long modificationTimeStageConfigFile;
modificationTimeStageConfigFile = _stageConfigFile.lastModified();

return modificationTimeStageConfigFile > _lastTimeReadingStageConfigFile;
}

Pattern[] arrayPattern = new Pattern[3];

String matchDN = matcherLine.group(1);
String matchFQAN = matcherLine.group(3);
String matchStoreUnit = matcherLine.group(5);

if ( matchFQAN != null ) {
if (matchStoreUnit != null) { //line: DN, FQAN, StoreUnit
arrayPattern[0] = Pattern.compile(matchDN);
arrayPattern[1] = Pattern.compile(matchFQAN);
arrayPattern[2] = Pattern.compile(matchStoreUnit);
regexList.add(arrayPattern);
} else { //line: DN, FQAN
arrayPattern[0] = Pattern.compile(matchDN);
arrayPattern[1] = Pattern.compile(matchFQAN);
arrayPattern[2] = null;
regexList.add(arrayPattern);
/**
* Check whether the user matches predicates, that is, whether the user is in the
* list of authorized users that are allowed to perform staging of the object in the
* given storage group.
*
* @param dn user's Distinguished Name
* @param fqanStr user's FQAN as a String
* @param storeUnit object's storage unit
* @return true if the user and object match predicates
*/
private boolean userMatchesPredicates(String dn,
String fqanStr,
String storeUnit,
String protocol) {
try {
_fileReadLock.lock();
for (Pattern[] regexLine : _regexList) {
if ( regexLine[0].matcher(dn).matches() ) {

if ( regexLine[1] == null ) {
return true; // line contains only DN; DN match -> STAGE allowed
} else if ( regexLine[1].matcher(fqanStr).matches() &&
(regexLine[2] == null || regexLine[2].matcher(storeUnit).matches()) &&
(regexLine[3] == null || regexLine[3].matcher(protocol).matches())) {
//three cases covered here:
//line contains DN and FQAN; DN and FQAN match -> STAGE allowed
//line contains DN, FQAN, storeUnit; DN, FQAN, storeUnit match -> STAGE allowed
//line contains DN, FQAN, storeUnit, protocol; all match -> STAGE allowed
return true;
}
} else { //line: DN
arrayPattern[0] = Pattern.compile(matchDN);
arrayPattern[1] = null;
arrayPattern[2] = null;
regexList.add(arrayPattern);
}
}
return regexList;
} finally {
_fileReadLock.unlock();
}
return false;
}

/**
* Read configuration file and create list of compiled patterns, containing DNs and FQANs(optionally)
* of the users that are allowed to perform staging,
* as well as storage group of the object to be staged (optionally).
*
* @param reader
* @return list of compiled patterns
* @throws IOException
* @throws PatternSyntaxException
*/
List<Pattern[]> readStageConfigFile(BufferedReader reader) throws IOException, PatternSyntaxException {

String line;
Matcher matcherLine;

List<Pattern[]> regexList = new ArrayList<>();

while ((line = reader.readLine()) != null) {

line = line.trim();
if ( line.startsWith("#") || line.isEmpty() ) { //commented or empty line
continue;
}

matcherLine = LINE_PATTERN.matcher(line);
if ( !matcherLine.matches() ) {
continue;
}

Pattern[] arrayPattern = new Pattern[4];

int i = 0;
for (String match : new String[] { matcherLine.group("dn"),
matcherLine.group("fqan"),
matcherLine.group("su"),
matcherLine.group("protocol")} ) {
if ( match != null ) {
if ( match.startsWith("!") ) {
arrayPattern[i] = Pattern.compile("(?!"+match.substring(1)+").*");
} else {
arrayPattern[i] = Pattern.compile(match);
}
}
++i;
}
regexList.add(arrayPattern);
}
return regexList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ private void enforceLifetimeLimit(PinManagerPinMessage message)
{
try {
Subject subject = task.getSubject();
return _checkStagePermission.canPerformStaging(subject, task.getFileAttributes()) ?
return _checkStagePermission.canPerformStaging(subject,
task.getFileAttributes(),
task.getProtocolInfo()) ?
RequestContainerV5.allStates :
RequestContainerV5.allStatesExceptStage;
} catch (PatternSyntaxException | IOException ex) {
Expand Down
4 changes: 3 additions & 1 deletion modules/dcache/src/main/java/org/dcache/util/Transfer.java
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,9 @@ public ListenableFuture<Void> selectPoolAsync(long timeout)
} else {
EnumSet<RequestContainerV5.RequestState> allowedStates;
try {
allowedStates = _checkStagePermission.canPerformStaging(_subject, fileAttributes)
allowedStates = _checkStagePermission.canPerformStaging(_subject,
fileAttributes,
protocolInfo)
? RequestContainerV5.allStates
: RequestContainerV5.allStatesExceptStage;
} catch (IOException e) {
Expand Down

0 comments on commit df629f2

Please sign in to comment.