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 20, 2017
1 parent c65ad88 commit b1fd306
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,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
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,16 @@ public void statefulServiceAuthorization() throws Throwable {
}));
this.host.testWait(ctx2);

// 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 @@ -729,9 +739,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);

// test user impersonation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ public void createUsers() throws Throwable {
* isn't created immediately, so this polls.
*/
private String loginUser(URI hostUri) throws Throwable {
URI usersLink = UriUtils.buildUri(hostUri, UserService.FACTORY_LINK);
// wait for factory availability
this.host.waitForReplicatedFactoryServiceAvailable(usersLink);

String basicAuth = BasicAuthenticationUtils.constructBasicAuth(adminUser, adminUser);
URI loginUri = UriUtils.buildUri(hostUri, ServiceUriPaths.CORE_AUTHN_BASIC);
AuthenticationRequest login = new AuthenticationRequest();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,13 @@ private void doRequestRateLimits() throws Throwable {
ri.limit = limit;
ri.options = EnumSet.of(RequestRateInfo.Option.PAUSE_PROCESSING);
this.host.setRequestRateLimit(userPath, ri);
this.host.assumeIdentity(userPath);

this.host.setSystemAuthorizationContext();
ServiceStat rateLimitStatBefore = getRateLimitOpCountStat();
this.host.resetSystemAuthorizationContext();

this.host.assumeIdentity(userPath);

if (rateLimitStatBefore == null) {
rateLimitStatBefore = new ServiceStat();
rateLimitStatBefore.latestValue = 0.0;
Expand All @@ -440,7 +444,10 @@ private void doRequestRateLimits() throws Throwable {
}
this.host.testWait(ctx2);
ctx2.logAfter();

this.host.setSystemAuthorizationContext();
ServiceStat rateLimitStatAfter = getRateLimitOpCountStat();
this.host.resetSystemAuthorizationContext();
assertTrue(rateLimitStatAfter.latestValue > rateLimitStatBefore.latestValue);

this.host.setMaintenanceIntervalMicros(
Expand Down Expand Up @@ -470,7 +477,9 @@ private void doRequestRateLimits() throws Throwable {
ctx3.logAfter();

// verify rate limiting did not happen
this.host.setSystemAuthorizationContext();
ServiceStat rateLimitStatExpectSame = getRateLimitOpCountStat();
this.host.resetSystemAuthorizationContext();
assertTrue(rateLimitStatAfter.latestValue == rateLimitStatExpectSame.latestValue);
}

Expand Down Expand Up @@ -2517,8 +2526,9 @@ private ServiceStat getODLStopCountStat() throws Throwable {

private ServiceStat getRateLimitOpCountStat() throws Throwable {
URI managementServiceUri = this.host.getManagementServiceUri();
return this.host.getServiceStats(managementServiceUri)
ServiceStat stats = this.host.getServiceStats(managementServiceUri)
.get(ServiceHostManagementService.STAT_NAME_RATE_LIMITED_OP_COUNT);
return stats;
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,14 @@ public void subscriptionsWithAuth() 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
Original file line number Diff line number Diff line change
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.ArrayList;
import java.util.EnumSet;
Expand All @@ -34,10 +38,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;
import com.vmware.xenon.services.common.ServiceUriPaths;

public class TestUtilityService extends BasicReusableHostTestCase {
Expand Down Expand Up @@ -823,4 +833,144 @@ public void statsKeyOrder() {
assertEquals("stat index 2", "keyCCC", statList.get(2));
}

@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());

}

}
Original file line number Diff line number Diff line change
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 @@ -247,6 +248,22 @@ public Collection<String> createRoles(ServiceHost target, String email) throws T
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 @@ -261,6 +278,16 @@ public Collection<String> createRoles(ServiceHost target, String email) throws T
// 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
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.Operation.AuthorizationContext;
import com.vmware.xenon.common.Operation.CompletionHandler;
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 @@ -1049,7 +1050,15 @@ 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.sender.sendStatsGetAndWait(serviceUri);
if (this.isAuthorizationEnabled()) {
this.setAuthorizationContext(ctx);
}
return stats.entries;
}

Expand Down
Loading

0 comments on commit b1fd306

Please sign in to comment.