Skip to content
This repository has been archived by the owner on Nov 9, 2020. It is now read-only.

Commit

Permalink
Add auth to UtilityService
Browse files Browse the repository at this point in the history
Extend auth check to UtiliyService endpoints: stats, config, subscription

Issue: VRXEN-5

Change-Id: I52a4b4a42731c244a97f97610dcaddb6837e67fb
  • Loading branch information
ttddyy committed Dec 14, 2017
1 parent 487d59d commit 756d893
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 3 deletions.
Expand Up @@ -57,7 +57,29 @@ public UtilityService setParent(Service parent) {

@Override
public void authorizeRequest(Operation op) {
op.complete();

String suffix = UriUtils.buildUriPath(UriUtils.URI_PATH_CHAR, UriUtils.getLastPathSegment(op.getUri()));

// allow access to ui endpoint
if (ServiceHost.SERVICE_URI_SUFFIX_UI.equals(suffix)) {
op.complete();
return;
}

ServiceDocument doc = new ServiceDocument();
if (this.parent.getOptions().contains(ServiceOption.FACTORY_ITEM)) {
doc.documentSelfLink = UriUtils.buildUriPath(UriUtils.getParentPath(this.parent.getSelfLink()), suffix);
} else {
doc.documentSelfLink = UriUtils.buildUriPath(this.parent.getSelfLink(), suffix);
}

doc.documentKind = Utils.buildKind(this.parent.getStateType());
if (getHost().isAuthorized(this.parent, doc, op)) {
op.complete();
return;
}

op.fail(Operation.STATUS_CODE_FORBIDDEN);
}

@Override
Expand Down
Expand Up @@ -513,6 +513,16 @@ public void statefulServiceAuthorization() throws Throwable {
}));
this.host.testWait();

// do GET on factory /stats, we should get 403
Operation statsGet = Operation.createGet(this.host,
ExampleService.FACTORY_LINK + ServiceHost.SERVICE_URI_SUFFIX_STATS);
this.host.sendAndWaitExpectFailure(statsGet, Operation.STATUS_CODE_FORBIDDEN);

// do GET on factory /config, we should get 403
Operation configGet = Operation.createGet(this.host,
ExampleService.FACTORY_LINK + ServiceHost.SERVICE_URI_SUFFIX_CONFIG);
this.host.sendAndWaitExpectFailure(configGet, Operation.STATUS_CODE_FORBIDDEN);

// Assume Jane's identity
this.host.assumeIdentity(this.userServicePath);
// add docs accessible by jane
Expand Down Expand Up @@ -555,9 +565,27 @@ public void statefulServiceAuthorization() throws Throwable {
// reset the auth context
OperationContext.setAuthorizationContext(null);

// do GET on utility suffixes in example child services, we should get 403
for (URI childUri : exampleServices.keySet()) {
statsGet = Operation.createGet(this.host,
childUri.getPath() + ServiceHost.SERVICE_URI_SUFFIX_STATS);
this.host.sendAndWaitExpectFailure(statsGet, Operation.STATUS_CODE_FORBIDDEN);
configGet = Operation.createGet(this.host,
childUri.getPath() + ServiceHost.SERVICE_URI_SUFFIX_CONFIG);
this.host.sendAndWaitExpectFailure(configGet, Operation.STATUS_CODE_FORBIDDEN);
}

// Assume Jane's identity through header auth token
String authToken = generateAuthToken(this.userServicePath);

// do GET on utility suffixes in example child services, we should get 200
for (URI childUri : exampleServices.keySet()) {
statsGet = Operation.createGet(this.host,
childUri.getPath() + ServiceHost.SERVICE_URI_SUFFIX_STATS);
statsGet.addRequestHeader(Operation.REQUEST_AUTH_TOKEN_HEADER, authToken);
this.host.sendAndWaitExpectSuccess(statsGet);
}

verifyJaneAccess(exampleServices, authToken);
}

Expand Down
Expand Up @@ -85,7 +85,9 @@ public void createUsers() throws Throwable {
private String loginUser(URI hostUri) throws Throwable {
URI usersLink = UriUtils.buildUri(hostUri, UserService.FACTORY_LINK);
// wait for factory availability
this.host.setSystemAuthorizationContext();
this.host.waitForReplicatedFactoryServiceAvailable(usersLink);
this.host.resetAuthorizationContext();

String basicAuth = constructBasicAuth(adminUser, adminUser);
URI loginUri = UriUtils.buildUri(hostUri, ServiceUriPaths.CORE_AUTHN_BASIC);
Expand Down
Expand Up @@ -299,11 +299,14 @@ public void testSubscriptionsWithAuth() throws Throwable {

}
};

hostWithAuth.setSystemAuthorizationContext();
Operation subscribe = Operation.createPost(UriUtils.buildUri(hostWithAuth, minimalServiceUUID));
subscribe.setReferer(hostWithAuth.getReferer());
ServiceSubscriber subscriber = new ServiceSubscriber();
subscriber.replayState = true;
hostWithAuth.startSubscriptionService(subscribe, notifyC, subscriber);
hostWithAuth.resetAuthorizationContext();
hostWithAuth.testWait(notifyContext);
} finally {
if (hostWithAuth != null) {
Expand Down
Expand Up @@ -14,8 +14,12 @@
package com.vmware.xenon.common;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;

import static com.vmware.xenon.common.ServiceHost.SERVICE_URI_SUFFIX_TEMPLATE;
import static com.vmware.xenon.common.ServiceHost.SERVICE_URI_SUFFIX_UI;

import java.net.URI;
import java.util.EnumSet;
import java.util.List;
Expand All @@ -32,10 +36,16 @@
import com.vmware.xenon.common.ServiceStats.TimeSeriesStats;
import com.vmware.xenon.common.ServiceStats.TimeSeriesStats.AggregationType;
import com.vmware.xenon.common.ServiceStats.TimeSeriesStats.TimeBin;
import com.vmware.xenon.common.test.AuthTestUtils;
import com.vmware.xenon.common.test.TestContext;
import com.vmware.xenon.common.test.TestRequestSender;
import com.vmware.xenon.common.test.TestRequestSender.FailureResponse;
import com.vmware.xenon.common.test.VerificationHost;
import com.vmware.xenon.services.common.AuthorizationContextService;
import com.vmware.xenon.services.common.ExampleService;
import com.vmware.xenon.services.common.ExampleService.ExampleServiceState;
import com.vmware.xenon.services.common.MinimalTestService;
import com.vmware.xenon.services.common.QueryTask.Query;

public class TestUtilityService extends BasicReusableHostTestCase {

Expand Down Expand Up @@ -586,4 +596,145 @@ public static void validateTimeSeriesStat(ServiceStat stat, long expectedBinDura
assertTrue(maxAvg > 0);
assertTrue(countPerMaxAvgBin >= 1);
}

@Test
public void endpointAuthorization() throws Throwable {
VerificationHost host = VerificationHost.create(0);
host.setAuthorizationService(new AuthorizationContextService());
host.setAuthorizationEnabled(true);
host.setMaintenanceIntervalMicros(TimeUnit.MILLISECONDS.toMicros(100));
host.start();

TestRequestSender sender = host.getTestRequestSender();

host.setSystemAuthorizationContext();
host.waitForReplicatedFactoryServiceAvailable(UriUtils.buildUri(host, ExampleService.FACTORY_LINK));

String exampleUser = "example@vmware.com";
String examplePass = "password";
TestContext authCtx = host.testCreate(1);
AuthorizationSetupHelper.create()
.setHost(host)
.setUserEmail(exampleUser)
.setUserPassword(examplePass)
.setResourceQuery(Query.Builder.create()
.addFieldClause(ServiceDocument.FIELD_NAME_KIND, Utils.buildKind(ExampleServiceState.class))
.build())
.setCompletion(authCtx.getCompletion())
.start();
authCtx.await();

// create a sample service
ExampleServiceState doc = new ExampleServiceState();
doc.name = "foo";
doc.documentSelfLink = "foo";

Operation post = Operation.createPost(host, ExampleService.FACTORY_LINK).setBody(doc);
ExampleServiceState postResult = sender.sendAndWait(post, ExampleServiceState.class);

host.resetAuthorizationContext();

URI factoryAvailableUri = UriUtils.buildAvailableUri(host, ExampleService.FACTORY_LINK);
URI factoryStatsUri = UriUtils.buildStatsUri(host, ExampleService.FACTORY_LINK);
URI factoryConfigUri = UriUtils.buildConfigUri(host, ExampleService.FACTORY_LINK);
URI factorySubscriptionUri = UriUtils.buildSubscriptionUri(host, ExampleService.FACTORY_LINK);
URI factoryTemplateUri = UriUtils.buildUri(host, UriUtils.buildUriPath(ExampleService.FACTORY_LINK, SERVICE_URI_SUFFIX_TEMPLATE));
URI factoryUiUri = UriUtils.buildUri(host, UriUtils.buildUriPath(ExampleService.FACTORY_LINK, SERVICE_URI_SUFFIX_UI));

URI serviceAvailableUri = UriUtils.buildAvailableUri(host, postResult.documentSelfLink);
URI serviceStatsUri = UriUtils.buildStatsUri(host, postResult.documentSelfLink);
URI serviceConfigUri = UriUtils.buildConfigUri(host, postResult.documentSelfLink);
URI serviceSubscriptionUri = UriUtils.buildSubscriptionUri(host, postResult.documentSelfLink);
URI serviceTemplateUri = UriUtils.buildUri(host, UriUtils.buildUriPath(postResult.documentSelfLink, SERVICE_URI_SUFFIX_TEMPLATE));
URI serviceUiUri = UriUtils.buildUri(host, UriUtils.buildUriPath(postResult.documentSelfLink, SERVICE_URI_SUFFIX_UI));

// check non-authenticated user receives forbidden response
FailureResponse failureResponse;
Operation uiOpResult;

// check factory endpoints
failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryAvailableUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryStatsUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryConfigUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(factorySubscriptionUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryTemplateUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

uiOpResult = sender.sendAndWait(Operation.createGet(factoryUiUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, uiOpResult.getStatusCode());

// check service endpoints
failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceAvailableUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceStatsUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceConfigUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceSubscriptionUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceTemplateUri));
assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

uiOpResult = sender.sendAndWait(Operation.createGet(serviceUiUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, uiOpResult.getStatusCode());


// check authenticated user does NOT receive forbidden response
AuthTestUtils.login(host, exampleUser, examplePass);

Operation response;

// check factory endpoints
response = sender.sendAndWait(Operation.createGet(factoryAvailableUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(factoryStatsUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(factoryConfigUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(factorySubscriptionUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(factoryTemplateUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(factoryUiUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());


// check service endpoints
response = sender.sendAndWait(Operation.createGet(serviceAvailableUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(serviceStatsUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(serviceConfigUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(serviceSubscriptionUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(serviceTemplateUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

response = sender.sendAndWait(Operation.createGet(serviceUiUri));
assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

}

}
Expand Up @@ -30,6 +30,7 @@
import com.vmware.xenon.common.ServiceHost;
import com.vmware.xenon.common.UriUtils;
import com.vmware.xenon.common.Utils;
import com.vmware.xenon.services.common.ExampleService;
import com.vmware.xenon.services.common.ExampleService.ExampleServiceState;
import com.vmware.xenon.services.common.QueryTask;
import com.vmware.xenon.services.common.QueryTask.Query;
Expand Down Expand Up @@ -269,6 +270,22 @@ public Collection<String> createRoles(ServiceHost target, String email, boolean
UriUtils.buildUriPath(ServiceUriPaths.CORE_AUTHZ_USERS, email))
.build());

// Create resource group to allow access on utility paths
String statsResourceGroupLink = createResourceGroup(target, "stats-resource-group",
Builder.create()
.addFieldClause(
ServiceDocument.FIELD_NAME_SELF_LINK,
ExampleService.FACTORY_LINK + ServiceHost.SERVICE_URI_SUFFIX_STATS)
.build());

String subscriptionsResourceGroupLink = createResourceGroup(target, "subs-resource-group",
Builder.create()
.addFieldClause(
ServiceDocument.FIELD_NAME_SELF_LINK,
ServiceUriPaths.CORE_LOCAL_QUERY_TASKS
+ ServiceHost.SERVICE_URI_SUFFIX_SUBSCRIPTIONS)
.build());

Collection<String> paths = new HashSet<>();

// Create roles tying these together
Expand All @@ -283,6 +300,16 @@ public Collection<String> createRoles(ServiceHost target, String email, boolean
// Create role authorizing access to the user's own query tasks
paths.add(createRole(target, userGroupLink, queryTaskResourceGroupLink,
new HashSet<>(Arrays.asList(Action.GET, Action.POST, Action.PATCH, Action.DELETE))));

// Create role authorizing access to /stats
paths.add(createRole(target, userGroupLink, statsResourceGroupLink,
new HashSet<>(
Arrays.asList(Action.GET, Action.POST, Action.PATCH, Action.DELETE))));

// Create role authorizing access to /subscriptions of query tasks
paths.add(createRole(target, userGroupLink, subscriptionsResourceGroupLink,
new HashSet<>(
Arrays.asList(Action.GET, Action.POST, Action.PATCH, Action.DELETE))));
return paths;
}

Expand Down
Expand Up @@ -79,6 +79,7 @@
import com.vmware.xenon.common.Operation.AuthorizationContext;
import com.vmware.xenon.common.Operation.CompletionHandler;
import com.vmware.xenon.common.Operation.OperationOption;
import com.vmware.xenon.common.OperationContext;
import com.vmware.xenon.common.Service;
import com.vmware.xenon.common.Service.Action;
import com.vmware.xenon.common.Service.ServiceOption;
Expand Down Expand Up @@ -1011,8 +1012,16 @@ public ServiceDocumentQueryResult getExpandedFactoryState(URI factoryUri) {
}

public Map<String, ServiceStat> getServiceStats(URI serviceUri) {
AuthorizationContext ctx = null;
if (this.isAuthorizationEnabled()) {
ctx = OperationContext.getAuthorizationContext();
this.setSystemAuthorizationContext();
}
ServiceStats stats = this.getServiceState(
null, ServiceStats.class, UriUtils.buildStatsUri(serviceUri));
if (this.isAuthorizationEnabled()) {
this.setAuthorizationContext(ctx);
}
return stats.entries;
}

Expand Down
Expand Up @@ -2428,6 +2428,7 @@ public void replicationWithAuthzCacheClear() throws Throwable {

VerificationHost groupHost = this.host.getPeerHost();

groupHost.setSystemAuthorizationContext();
// wait for auth related services to be stabilized
groupHost.waitForReplicatedFactoryServiceAvailable(
UriUtils.buildUri(groupHost, UserService.FACTORY_LINK));
Expand All @@ -2445,7 +2446,7 @@ public void replicationWithAuthzCacheClear() throws Throwable {
String bazUserLink = UriUtils.buildUriPath(ServiceUriPaths.CORE_AUTHZ_USERS,
"baz@vmware.com");

groupHost.setSystemAuthorizationContext();


// create user, user-group, resource-group, role for foo@vmware.com
// user: /core/authz/users/foo@vmware.com
Expand Down Expand Up @@ -2655,7 +2656,7 @@ private void populateAuthCacheInAllPeers(AuthorizationContext authContext) throw

// based on the role created in test, all users have access to ExampleService
this.host.sendAndWaitExpectSuccess(
Operation.createGet(UriUtils.buildStatsUri(peer, ExampleService.FACTORY_LINK)));
Operation.createGet(UriUtils.buildUri(peer, ExampleService.FACTORY_LINK)));
}

this.host.waitFor("Timeout waiting for correct auth cache state",
Expand Down

0 comments on commit 756d893

Please sign in to comment.