Skip to content

Commit 89bb8bf

Browse files
committed
HADOOP-14442. Owner support for ranger-wasb integration. Contributed by Varada Hemeswari
1 parent bd6a217 commit 89bb8bf

File tree

6 files changed

+247
-80
lines changed

6 files changed

+247
-80
lines changed

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/NativeAzureFileSystem.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public FolderRenamePending(Path redoFile, NativeAzureFileSystem fs)
200200
JsonNode oldFolderName = json.get("OldFolderName");
201201
JsonNode newFolderName = json.get("NewFolderName");
202202
if (oldFolderName == null || newFolderName == null) {
203-
this.committed = false;
203+
this.committed = false;
204204
} else {
205205
this.srcKey = oldFolderName.textValue();
206206
this.dstKey = newFolderName.textValue();
@@ -349,7 +349,7 @@ public String makeRenamePendingFileContents() {
349349

350350
return contents;
351351
}
352-
352+
353353
/**
354354
* This is an exact copy of org.codehaus.jettison.json.JSONObject.quote
355355
* method.
@@ -639,7 +639,7 @@ public String getScheme() {
639639
return "wasb";
640640
}
641641

642-
642+
643643
/**
644644
* <p>
645645
* A {@link FileSystem} for reading and writing files stored on <a
@@ -1441,12 +1441,14 @@ private void performAuthCheck(Path requestingAccessForPath, WasbAuthorizationOpe
14411441
requestingAccessForPath = requestingAccessForPath.makeQualified(getUri(), getWorkingDirectory());
14421442
originalPath = originalPath.makeQualified(getUri(), getWorkingDirectory());
14431443

1444-
if (!this.authorizer.authorize(requestingAccessForPath.toString(), accessType.toString())) {
1444+
String owner = getOwnerForPath(requestingAccessForPath);
1445+
1446+
if (!this.authorizer.authorize(requestingAccessForPath.toString(), accessType.toString(), owner)) {
14451447
throw new WasbAuthorizationException(operation
14461448
+ " operation for Path : " + originalPath.toString() + " not allowed");
14471449
}
14481450

1449-
}
1451+
}
14501452
}
14511453

14521454
/**
@@ -3173,4 +3175,40 @@ private static String encodeKey(String aKey) {
31733175
// Return to the caller with the randomized key.
31743176
return randomizedKey;
31753177
}
3178+
3179+
/*
3180+
* Helper method to retrieve owner information for a given path.
3181+
* The method returns empty string in case the file is not found or the metadata does not contain owner information
3182+
*/
3183+
@VisibleForTesting
3184+
public String getOwnerForPath(Path absolutePath) throws IOException {
3185+
String owner = "";
3186+
FileMetadata meta = null;
3187+
String key = pathToKey(absolutePath);
3188+
try {
3189+
3190+
meta = store.retrieveMetadata(key);
3191+
3192+
if (meta != null) {
3193+
owner = meta.getPermissionStatus().getUserName();
3194+
LOG.debug("Retrieved '{}' as owner for path - {}", owner, absolutePath);
3195+
} else {
3196+
// meta will be null if file/folder doen not exist
3197+
LOG.debug("Cannot find file/folder - '{}'. Returning owner as empty string", absolutePath);
3198+
}
3199+
} catch(IOException ex) {
3200+
3201+
Throwable innerException = NativeAzureFileSystemHelper.checkForAzureStorageException(ex);
3202+
boolean isfileNotFoundException = innerException instanceof StorageException
3203+
&& NativeAzureFileSystemHelper.isFileNotFoundException((StorageException) innerException);
3204+
3205+
// should not throw when the exception is related to blob/container/file/folder not found
3206+
if (!isfileNotFoundException) {
3207+
String errorMsg = "Could not retrieve owner information for path - " + absolutePath;
3208+
LOG.error(errorMsg);
3209+
throw new IOException(errorMsg, ex);
3210+
}
3211+
}
3212+
return owner;
3213+
}
31763214
}

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/RemoteWasbAuthorizerImpl.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ public class RemoteWasbAuthorizerImpl implements WasbAuthorizerInterface {
8787
private static final String DELEGATION_TOKEN_QUERY_PARAM_NAME =
8888
"delegation";
8989

90+
/**
91+
* Query parameter name for sending owner of the specific resource {@value}
92+
*/
93+
private static final String WASB_RESOURCE_OWNER_QUERY_PARAM_NAME =
94+
"wasb_resource_owner";
95+
9096
private WasbRemoteCallHelper remoteCallHelper = null;
9197
private String delegationToken;
9298
private boolean isSecurityEnabled;
@@ -119,7 +125,7 @@ public void init(Configuration conf)
119125
}
120126

121127
@Override
122-
public boolean authorize(String wasbAbsolutePath, String accessType)
128+
public boolean authorize(String wasbAbsolutePath, String accessType, String resourceOwner)
123129
throws WasbAuthorizationException, IOException {
124130

125131
try {
@@ -140,6 +146,10 @@ public boolean authorize(String wasbAbsolutePath, String accessType)
140146
uriBuilder.addParameter(DELEGATION_TOKEN_QUERY_PARAM_NAME,
141147
delegationToken);
142148
}
149+
if (resourceOwner != null && StringUtils.isNotEmpty(resourceOwner)) {
150+
uriBuilder.addParameter(WASB_RESOURCE_OWNER_QUERY_PARAM_NAME,
151+
resourceOwner);
152+
}
143153

144154
String responseBody = null;
145155
UserGroupInformation ugi = UserGroupInformation.getCurrentUser();

hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/WasbAuthorizerInterface.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ public void init(Configuration conf)
4343
4444
* @param wasbAbolutePath : Absolute WASB Path used for access.
4545
* @param accessType : Type of access
46+
* @param owner : owner of the file/folder specified in the wasb path
4647
* @return : true - If access allowed false - If access is not allowed.
4748
* @throws WasbAuthorizationException - On authorization exceptions
4849
* @throws IOException - When not able to reach the authorizer
4950
*/
50-
boolean authorize(String wasbAbolutePath, String accessType)
51+
boolean authorize(String wasbAbolutePath, String accessType, String owner)
5152
throws WasbAuthorizationException, IOException;
5253
}

hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/MockWasbAuthorizerImpl.java

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import java.util.Map;
2323
import java.util.regex.Pattern;
2424

25+
import org.apache.hadoop.security.UserGroupInformation;
2526
import org.apache.hadoop.conf.Configuration;
27+
import org.apache.hadoop.util.StringUtils;
2628
import org.apache.hadoop.fs.Path;
2729

2830
/**
@@ -32,8 +34,9 @@
3234
public class MockWasbAuthorizerImpl implements WasbAuthorizerInterface {
3335

3436
private Map<AuthorizationComponent, Boolean> authRules;
37+
private boolean performOwnerMatch;
3538

36-
// The full qualified URL to the root directory
39+
// The full qualified URL to the root directory
3740
private String qualifiedPrefixUrl;
3841

3942
public MockWasbAuthorizerImpl(NativeAzureFileSystem fs) {
@@ -43,34 +46,63 @@ public MockWasbAuthorizerImpl(NativeAzureFileSystem fs) {
4346

4447
@Override
4548
public void init(Configuration conf) {
49+
init(conf, false);
50+
}
51+
52+
/*
53+
authorization matches owner with currentUserShortName while evaluating auth rules
54+
if currentUserShortName is set to a string that is not empty
55+
*/
56+
public void init(Configuration conf, boolean matchOwner) {
4657
authRules = new HashMap<AuthorizationComponent, Boolean>();
58+
this.performOwnerMatch = matchOwner;
4759
}
4860

4961
public void addAuthRule(String wasbAbsolutePath,
5062
String accessType, boolean access) {
51-
52-
wasbAbsolutePath = qualifiedPrefixUrl + wasbAbsolutePath;
53-
54-
AuthorizationComponent component = wasbAbsolutePath.endsWith("*")
63+
wasbAbsolutePath = qualifiedPrefixUrl + wasbAbsolutePath;
64+
AuthorizationComponent component = wasbAbsolutePath.endsWith("*")
5565
? new AuthorizationComponent("^" + wasbAbsolutePath.replace("*", ".*"), accessType)
5666
: new AuthorizationComponent(wasbAbsolutePath, accessType);
5767

5868
this.authRules.put(component, access);
5969
}
6070

6171
@Override
62-
public boolean authorize(String wasbAbsolutePath, String accessType)
72+
public boolean authorize(String wasbAbsolutePath, String accessType, String owner)
6373
throws WasbAuthorizationException {
6474

6575
if (wasbAbsolutePath.endsWith(NativeAzureFileSystem.FolderRenamePending.SUFFIX)) {
6676
return true;
6777
}
6878

79+
String currentUserShortName = "";
80+
if (this.performOwnerMatch) {
81+
try {
82+
UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
83+
currentUserShortName = ugi.getShortUserName();
84+
} catch (Exception e) {
85+
//no op
86+
}
87+
}
88+
89+
// In case of root("/"), owner match does not happen because owner is returned as empty string.
90+
// we try to force owner match just for purpose of tests to make sure all operations work seemlessly with owner.
91+
if (this.performOwnerMatch
92+
&& StringUtils.equalsIgnoreCase(wasbAbsolutePath, qualifiedPrefixUrl + "/")) {
93+
owner = currentUserShortName;
94+
}
95+
96+
boolean shouldEvaluateOwnerAccess = owner != null && !owner.isEmpty()
97+
&& this.performOwnerMatch;
98+
99+
boolean isOwnerMatch = StringUtils.equalsIgnoreCase(currentUserShortName, owner);
100+
69101
AuthorizationComponent component =
70102
new AuthorizationComponent(wasbAbsolutePath, accessType);
71103

72104
if (authRules.containsKey(component)) {
73-
return authRules.get(component);
105+
return shouldEvaluateOwnerAccess ? isOwnerMatch && authRules.get(component) : authRules.get(component);
74106
} else {
75107
// Regex-pattern match if we don't have a straight match
76108
for (Map.Entry<AuthorizationComponent, Boolean> entry : authRules.entrySet()) {
@@ -79,12 +111,16 @@ public boolean authorize(String wasbAbsolutePath, String accessType)
79111
String keyAccess = key.getAccessType();
80112

81113
if (keyPath.endsWith("*") && Pattern.matches(keyPath, wasbAbsolutePath) && keyAccess.equals(accessType)) {
82-
return entry.getValue();
114+
return shouldEvaluateOwnerAccess ? isOwnerMatch && entry.getValue() : entry.getValue();
83115
}
84116
}
85117
return false;
86118
}
87119
}
120+
121+
public void deleteAllAuthRules() {
122+
authRules.clear();
123+
}
88124
}
89125

90126
class AuthorizationComponent {

0 commit comments

Comments
 (0)