use of co.cask.cdap.proto.id.KerberosPrincipalId in project cdap by caskdata.
the class ApplicationLifecycleService method deployApp.
private ApplicationWithPrograms deployApp(NamespaceId namespaceId, @Nullable String appName, @Nullable String appVersion, @Nullable String configStr, ProgramTerminator programTerminator, ArtifactDetail artifactDetail, @Nullable KerberosPrincipalId ownerPrincipal, boolean updateSchedules) throws Exception {
// Now to deploy an app, we need ADMIN privilege on the owner principal if it is present, and also ADMIN on the app
// But since at this point, app name is unknown to us, so the enforcement on the app is happening in the deploy
// pipeline - LocalArtifactLoaderStage
// need to enforce on the principal id if impersonation is involved
KerberosPrincipalId effectiveOwner = SecurityUtil.getEffectiveOwner(ownerAdmin, namespaceId, ownerPrincipal == null ? null : ownerPrincipal.getPrincipal());
Principal requestingUser = authenticationContext.getPrincipal();
// impersonated principal
if (effectiveOwner != null) {
authorizationEnforcer.enforce(effectiveOwner, requestingUser, Action.ADMIN);
}
ApplicationClass appClass = Iterables.getFirst(artifactDetail.getMeta().getClasses().getApps(), null);
if (appClass == null) {
throw new InvalidArtifactException(String.format("No application class found in artifact '%s' in namespace '%s'.", artifactDetail.getDescriptor().getArtifactId(), namespaceId));
}
// deploy application with newly added artifact
AppDeploymentInfo deploymentInfo = new AppDeploymentInfo(artifactDetail.getDescriptor(), namespaceId, appClass.getClassName(), appName, appVersion, configStr, ownerPrincipal, updateSchedules);
Manager<AppDeploymentInfo, ApplicationWithPrograms> manager = managerFactory.create(programTerminator);
// TODO: (CDAP-3258) Manager needs MUCH better error handling.
ApplicationWithPrograms applicationWithPrograms;
try {
applicationWithPrograms = manager.deploy(deploymentInfo).get();
} catch (ExecutionException e) {
Throwables.propagateIfPossible(e.getCause(), Exception.class);
throw Throwables.propagate(e.getCause());
}
return applicationWithPrograms;
}
use of co.cask.cdap.proto.id.KerberosPrincipalId in project cdap by caskdata.
the class AppLifecycleHttpHandler method deployApplication.
private BodyConsumer deployApplication(final HttpResponder responder, final NamespaceId namespace, final String appId, final String archiveName, final String configString, @Nullable final String ownerPrincipal, final boolean updateSchedules) throws IOException {
Id.Namespace idNamespace = Id.Namespace.fromEntityId(namespace);
Location namespaceHomeLocation = namespacedLocationFactory.get(namespace);
if (!namespaceHomeLocation.exists()) {
String msg = String.format("Home directory %s for namespace %s not found", namespaceHomeLocation, namespace.getNamespace());
LOG.error(msg);
responder.sendString(HttpResponseStatus.NOT_FOUND, msg);
return null;
}
if (archiveName == null || archiveName.isEmpty()) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, String.format("%s header not present. Please include the header and set its value to the jar name.", ARCHIVE_NAME_HEADER), new DefaultHttpHeaders().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE));
return null;
}
// TODO: (CDAP-3258) error handling needs to be refactored here, should be able just to throw the exception,
// but the caller catches all exceptions and responds with a 500
final Id.Artifact artifactId;
try {
artifactId = Id.Artifact.parse(idNamespace, archiveName);
} catch (IllegalArgumentException e) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
return null;
}
KerberosPrincipalId ownerPrincipalId = ownerPrincipal == null ? null : new KerberosPrincipalId(ownerPrincipal);
// Store uploaded content to a local temp file
String namespacesDir = configuration.get(Constants.Namespace.NAMESPACES_DIR);
File localDataDir = new File(configuration.get(Constants.CFG_LOCAL_DATA_DIR));
File namespaceBase = new File(localDataDir, namespacesDir);
File tempDir = new File(new File(namespaceBase, namespace.getNamespace()), configuration.get(Constants.AppFabric.TEMP_DIR)).getAbsoluteFile();
if (!DirUtils.mkdirs(tempDir)) {
throw new IOException("Could not create temporary directory at: " + tempDir);
}
final KerberosPrincipalId finalOwnerPrincipalId = ownerPrincipalId;
return new AbstractBodyConsumer(File.createTempFile("app-", ".jar", tempDir)) {
@Override
protected void onFinish(HttpResponder responder, File uploadedFile) {
try {
// deploy app
ApplicationWithPrograms app = applicationLifecycleService.deployAppAndArtifact(namespace, appId, artifactId, uploadedFile, configString, finalOwnerPrincipalId, createProgramTerminator(), updateSchedules);
LOG.info("Successfully deployed app {} in namespace {} from artifact {} with configuration {} and " + "principal {}", app.getApplicationId().getApplication(), namespace.getNamespace(), artifactId, configString, finalOwnerPrincipalId);
responder.sendString(HttpResponseStatus.OK, String.format("Successfully deployed app %s", app.getApplicationId().getApplication()));
} catch (InvalidArtifactException e) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
} catch (ArtifactAlreadyExistsException e) {
responder.sendString(HttpResponseStatus.CONFLICT, String.format("Artifact '%s' already exists. Please use the API that creates an application from an existing artifact. " + "If you are trying to replace the artifact, please delete it and then try again.", artifactId));
} catch (WriteConflictException e) {
// don't really expect this to happen. It means after multiple retries there were still write conflicts.
LOG.warn("Write conflict while trying to add artifact {}.", artifactId, e);
responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Write conflict while adding artifact. This can happen if multiple requests to add " + "the same artifact occur simultaneously. Please try again.");
} catch (UnauthorizedException e) {
responder.sendString(HttpResponseStatus.FORBIDDEN, e.getMessage());
} catch (ConflictException e) {
responder.sendString(HttpResponseStatus.CONFLICT, e.getMessage());
} catch (Exception e) {
LOG.error("Deploy failure", e);
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
}
}
};
}
use of co.cask.cdap.proto.id.KerberosPrincipalId in project cdap by caskdata.
the class AuthorizationStreamAdmin method create.
@Nullable
@Override
public StreamConfig create(StreamId streamId, @Nullable Properties props) throws Exception {
String specifiedOwnerPrincipal = props != null && props.containsKey(Constants.Security.PRINCIPAL) ? props.getProperty(Constants.Security.PRINCIPAL) : null;
// need to enforce on the principal id if impersonation is involved
KerberosPrincipalId effectiveOwner = SecurityUtil.getEffectiveOwner(ownerAdmin, streamId.getNamespaceId(), specifiedOwnerPrincipal);
Principal requestingUser = authenticationContext.getPrincipal();
if (effectiveOwner != null) {
authorizationEnforcer.enforce(effectiveOwner, requestingUser, Action.ADMIN);
}
ensureAccess(streamId, Action.ADMIN);
return delegate.create(streamId, props);
}
use of co.cask.cdap.proto.id.KerberosPrincipalId in project cdap by caskdata.
the class OwnerStoreTest method test.
@Test
public void test() throws Exception {
OwnerStore ownerStore = getOwnerStore();
StreamId streamId = NamespaceId.DEFAULT.stream("fooStream");
// No owner info should exist for above stream
Assert.assertNull(ownerStore.getOwner(streamId));
// delete behavior is idempotent, so won't throw NotFoundException
ownerStore.delete(streamId);
// Storing an owner for the first time should work
KerberosPrincipalId kerberosPrincipalId = new KerberosPrincipalId("alice/somehost@SOMEKDC.NET");
ownerStore.add(streamId, kerberosPrincipalId);
// owner principal should exists
Assert.assertTrue(ownerStore.exists(streamId));
// Should be able to get the principal back
Assert.assertEquals(kerberosPrincipalId, ownerStore.getOwner(streamId));
// Should not be able to update the owner principal
try {
ownerStore.add(streamId, new KerberosPrincipalId("bob@SOMEKDC.NET"));
Assert.fail();
} catch (AlreadyExistsException e) {
// expected
}
// Should not be able to update the owner principal
try {
ownerStore.add(streamId, new KerberosPrincipalId("somePrincipal"));
Assert.fail();
} catch (AlreadyExistsException e) {
// expected
}
// trying to update with invalid principal should fail early on with IllegalArgumentException
try {
ownerStore.add(streamId, new KerberosPrincipalId("b@ob@SOMEKDC.NET"));
Assert.fail();
} catch (IllegalArgumentException e) {
// expected
}
// Trying to store owner information for unsupported type should fail
try {
ownerStore.add(NamespaceId.DEFAULT.topic("anotherStream"), new KerberosPrincipalId("somePrincipal"));
Assert.fail();
} catch (IllegalArgumentException e) {
// expected
}
// delete the owner information
ownerStore.delete(streamId);
Assert.assertFalse(ownerStore.exists(streamId));
Assert.assertNull(ownerStore.getOwner(streamId));
}
use of co.cask.cdap.proto.id.KerberosPrincipalId in project cdap by caskdata.
the class AuthorizationUtil method getAppAuthorizingUser.
/**
* Helper function to get the authorizing user for app deployment, the authorzing user will be the app owner if it
* is present. If not, it will be the namespace owner. If that is also not present, it will be the user who is making
* the request
*/
public static String getAppAuthorizingUser(OwnerAdmin ownerAdmin, AuthenticationContext authenticationContext, ApplicationId applicationId, @Nullable KerberosPrincipalId appOwner) throws IOException {
KerberosPrincipalId effectiveOwner = SecurityUtil.getEffectiveOwner(ownerAdmin, applicationId.getNamespaceId(), appOwner == null ? null : appOwner.getPrincipal());
// CDAP-13154 If impersonation is configured for either the application or namespace the effective owner will be
// a kerberos principal which can have different form
// (refer: https://docs.oracle.com/cd/E21455_01/common/tutorials/kerberos_principal.html). For example it can be
// a complete principal name (alice/somehost.net@someREALM). For authorization we need the enforcement to happen
// on the username and not the complete principal. The user name is the shortname of the principal so return the
// shortname as authorizing user.
String appAuthorizingUser = effectiveOwner != null ? new KerberosName(effectiveOwner.getPrincipal()).getShortName() : authenticationContext.getPrincipal().getName();
LOG.trace("Returning {} as authorizing app user for {}", appAuthorizingUser, applicationId);
return appAuthorizingUser;
}
Aggregations