/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.esri.gpt.server.assertion.components;
import com.esri.gpt.catalog.context.CatalogIndexException;
import com.esri.gpt.catalog.lucene.LuceneIndexAdapter;
import com.esri.gpt.framework.context.ConfigurationException;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.security.identity.AuthenticationStatus;
import com.esri.gpt.framework.security.identity.NotAuthorizedException;
import com.esri.gpt.framework.security.metadata.MetadataAcl;
import com.esri.gpt.framework.security.principal.RoleSet;
import com.esri.gpt.framework.security.principal.User;
import com.esri.gpt.framework.util.Val;
import com.esri.gpt.server.assertion.exception.AsnInsufficientPrivilegeException;
import com.esri.gpt.server.assertion.exception.AsnUnestablishedUserException;
import com.esri.gpt.server.assertion.index.AsnUserPart;
import com.esri.gpt.server.assertion.index.Assertion;
import java.sql.Timestamp;
/**
* Handles authorization requests for an assertion operation.
*/
public class AsnAuthorizer {
/** class variables ========================================================= */
private static final String ACTION_CREATE = "create";
private static final String ACTION_DELETE = "delete";
private static final String ACTION_DISABLE = "disable";
private static final String ACTION_ENABLE = "enable";
private static final String ACTION_QUERY = "query";
private static final String ACTION_UPDATE = "update";
/** instance variables ====================================================== */
private boolean wasUserEstablished = false;
/** constructors ============================================================ */
/** Default constructor. */
public AsnAuthorizer() {}
/** properties ============================================================== */
/**
* Gets the flag indicating whether or not the user was established.
* @return <code>true</code> if the user was established
*/
public boolean getWasUserEstablished() {
return this.wasUserEstablished;
}
/**
* Sets the flag indicating whether or not the user was established.
* @param wasUserEstablished <code>true</code> if the user was established
*/
public void setWasUserEstablished(boolean wasUserEstablished) {
this.wasUserEstablished = wasUserEstablished;
}
/** methods ================================================================= */
/**
* Authorizes a create, update, delete or query based operation.
* @param context the assertion operation context
* @param assertion the active assertion
* @param action the action
* @throws NotAuthorizedException if authentication was required
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
private void authorizeAction(AsnContext context, Assertion assertion, String action)
throws NotAuthorizedException, AsnInsufficientPrivilegeException {
// ensure an authorization policy
AsnOperation operation = context.getOperation();
if (operation.getAuthPolicy() == null) {
String msg = "An authorization policy was not configured.";
throw new ConfigurationException(msg);
}
// check the user, ensure an authenticated user if required
User user = context.getRequestContext().getUser();
boolean userWasAuthenticated = false;
if ((user != null) && user.getAuthenticationStatus().getWasAuthenticated()) {
userWasAuthenticated = true;
}
if (operation.getAuthPolicy().getAuthenticationRequired() && !userWasAuthenticated) {
throw new NotAuthorizedException("Not authorized.");
}
// determine the principals
AsnPrincipals principals = null;
boolean isWrite = false;
if (action.equals(AsnAuthorizer.ACTION_CREATE)) {
isWrite = true;
principals = operation.getAuthPolicy().getCreatePrincipals();
if (principals == null) {
String msg = "Create principals were not configured.";
throw new ConfigurationException(msg);
}
} else if (action.equals(AsnAuthorizer.ACTION_DELETE)) {
isWrite = true;
principals = operation.getAuthPolicy().getDeletePrincipals();
if (principals == null) {
String msg = "Delete principals were not configured.";
throw new ConfigurationException(msg);
}
} else if (action.equals(AsnAuthorizer.ACTION_ENABLE) ||
action.equals(AsnAuthorizer.ACTION_DISABLE)) {
isWrite = true;
principals = operation.getAuthPolicy().getEnableDisablePrincipals();
if (principals == null) {
String msg = "Enable/Disable principals were not configured.";
throw new ConfigurationException(msg);
}
} else if (action.equals(AsnAuthorizer.ACTION_QUERY)) {
principals = operation.getAuthPolicy().getQueryPrincipals();
if (principals == null) {
String msg = "Query principals were not configured.";
throw new ConfigurationException(msg);
}
} else if (action.equals(AsnAuthorizer.ACTION_UPDATE)) {
isWrite = true;
principals = operation.getAuthPolicy().getQueryPrincipals();
if (principals == null) {
String msg = "Query principals were not configured.";
throw new ConfigurationException(msg);
}
}
// hard check to ensure an authenticated user for any modifications
// (regardless of configuration)
if (isWrite && !userWasAuthenticated) {
throw new AsnInsufficientPrivilegeException();
}
// check "any user" user privilege
if (principals.contains(AsnConstants.PRINCIPAL_ANY)) {
return;
}
// check administrator privilege
if (userWasAuthenticated) {
if (principals.contains(AsnConstants.PRINCIPAL_ADMINISTRATOR)) {
RoleSet roles = user.getAuthenticationStatus().getAuthenticatedRoles();
if (roles.hasRole("gptAdministrator")) {
return;
}
}
}
// check for ownership
if (userWasAuthenticated && (assertion != null) && assertion.getWasReadFromIndex()) {
if (principals.contains(AsnConstants.PRINCIPAL_OWNER)) {
String asnUserKey = Val.chkStr(assertion.getUserPart().getKey());
String userKey = Val.chkStr(user.getKey());
if ((asnUserKey.length() > 0) && asnUserKey.equals(userKey)) {
return;
}
}
}
throw new AsnInsufficientPrivilegeException();
}
/**
* Authorizes a create operation.
* @param context the assertion operation context
* assertion the active assertion
* @throws NotAuthorizedException if authentication was required
* @throws CatalogIndexException indicates an I/O error with the resource index
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
public void authorizeCreate(AsnContext context, Assertion assertion)
throws NotAuthorizedException, CatalogIndexException, AsnInsufficientPrivilegeException {
this.authorizeAction(context,assertion,AsnAuthorizer.ACTION_CREATE);
this.authorizeResourceAccess(context);
}
/**
* Authorizes a delete operation.
* @param context the assertion operation context
* @param assertion the active assertion
* @throws NotAuthorizedException if authentication was required
* @throws CatalogIndexException indicates an I/O error with the resource index
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
public void authorizeDelete(AsnContext context, Assertion assertion)
throws NotAuthorizedException, CatalogIndexException, AsnInsufficientPrivilegeException {
this.authorizeAction(context,assertion,AsnAuthorizer.ACTION_DELETE);
this.authorizeResourceAccess(context);
}
/**
* Authorizes a disable operation.
* @param context the assertion operation context
* @param assertion the active assertion
* @throws NotAuthorizedException if authentication was required
* @throws CatalogIndexException indicates an I/O error with the resource index
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
public void authorizeDisable(AsnContext context, Assertion assertion)
throws NotAuthorizedException, CatalogIndexException, AsnInsufficientPrivilegeException {
this.authorizeAction(context,assertion,AsnAuthorizer.ACTION_DISABLE);
this.authorizeResourceAccess(context);
}
/**
* Authorizes an enable operation.
* @param context the assertion operation context
* @param assertion the active assertion
* @throws NotAuthorizedException if authentication was required
* @throws CatalogIndexException indicates an I/O error with the resource index
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
public void authorizeEnable(AsnContext context, Assertion assertion)
throws NotAuthorizedException, CatalogIndexException, AsnInsufficientPrivilegeException {
this.authorizeAction(context,assertion,AsnAuthorizer.ACTION_ENABLE);
this.authorizeResourceAccess(context);
}
/**
* Authorizes a query based operation.
* @param context the assertion operation context
* @throws NotAuthorizedException if authentication was required
* @throws CatalogIndexException indicates an I/O error with the resource index
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
public void authorizeQuery(AsnContext context)
throws NotAuthorizedException, CatalogIndexException, AsnInsufficientPrivilegeException {
this.authorizeAction(context,null,AsnAuthorizer.ACTION_QUERY);
this.authorizeResourceAccess(context);
}
/**
* Authorizes an update operation.
* @param context the assertion operation context
* @param assertion the active assertion
* @throws NotAuthorizedException if authentication was required
* @throws CatalogIndexException indicates an I/O error with the resource index
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
public void authorizeUpdate(AsnContext context, Assertion assertion)
throws NotAuthorizedException, CatalogIndexException, AsnInsufficientPrivilegeException {
this.authorizeAction(context,assertion,AsnAuthorizer.ACTION_UPDATE);
this.authorizeResourceAccess(context);
}
/**
* Authorizes user access to a subject resource.
* @param context the assertion operation context
* @throws CatalogIndexException indicates an I/O error with the resource index
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
*/
private void authorizeResourceAccess(AsnContext context)
throws CatalogIndexException, AsnInsufficientPrivilegeException {
String resourceId = context.getOperation().getSystemPart().getResourceId();
if ((resourceId == null) || (resourceId.length() == 0)) {
return;
}
// ensure acl access
RequestContext rContext = context.getRequestContext();
AuthenticationStatus auth = rContext.getUser().getAuthenticationStatus();
boolean bAdmin = auth.getAuthenticatedRoles().hasRole("gptAdministrator");
if (!bAdmin) {
MetadataAcl acl = new MetadataAcl(rContext);
if (!acl.isPolicyUnrestricted()) {
LuceneIndexAdapter adapter = new LuceneIndexAdapter(rContext);
String[] resourceAcls = adapter.queryAcls(resourceId);
if ((resourceAcls != null) && (resourceAcls.length > 0)) {
String[] userAcls = acl.makeUserAcl();
if ((userAcls != null) && (userAcls.length > 0)) {
for (String resourcePrincipal: resourceAcls) {
for (String userPrincipal: userAcls) {
if (resourcePrincipal.equalsIgnoreCase(userPrincipal)) {
return;
}
}
}
}
throw new AsnInsufficientPrivilegeException();
}
}
}
// ensure the existence of a local resource id
if (!context.getOperation().getAuthPolicy().getAllowNonLocalResourceIds()) {
LuceneIndexAdapter adapter = new LuceneIndexAdapter(rContext);
Timestamp ts = adapter.queryModifiedDate(resourceId);
if (ts == null) {
throw new CatalogIndexException("Invalid resource id: "+resourceId);
}
}
}
/**
* Determines if the user can perform an action.
* @param context the assertion operation context
* @param policy the authorization policy
* @param assertion the active assertion
* @param action the action
* @return true if the user can perform the action
*/
private boolean canAct(AsnContext context, AsnAuthPolicy policy, Assertion assertion, String action) {
// ensure an authorization policy
if (policy == null) {
return false;
}
// check the user, ensure an authenticated user if required
User user = context.getRequestContext().getUser();
boolean userWasAuthenticated = false;
if ((user != null) && user.getAuthenticationStatus().getWasAuthenticated()) {
userWasAuthenticated = true;
}
if (policy.getAuthenticationRequired() && !userWasAuthenticated) {
return false;
}
// determine the write principals (creation or modification)
AsnPrincipals principals = null;
boolean isWrite = false;
if (action.equals(AsnAuthorizer.ACTION_CREATE)) {
isWrite = true;
principals = policy.getCreatePrincipals();
} else if (action.equals(AsnAuthorizer.ACTION_DELETE)) {
isWrite = true;
principals = policy.getDeletePrincipals();
if (assertion == null) return false;
} else if (action.equals(AsnAuthorizer.ACTION_DISABLE)) {
isWrite = true;
principals = policy.getEnableDisablePrincipals();
if (assertion == null) return false;
if (!assertion.getSystemPart().getEnabled()) return false;
} else if (action.equals(AsnAuthorizer.ACTION_ENABLE)){
isWrite = true;
principals = policy.getEnableDisablePrincipals();
if (assertion.getSystemPart().getEnabled()) return false;
if (assertion == null) return false;
} else if (action.equals(AsnAuthorizer.ACTION_QUERY)) {
principals = policy.getQueryPrincipals();
} else if (action.equals(AsnAuthorizer.ACTION_UPDATE)) {
isWrite = true;
principals = policy.getQueryPrincipals();
if (assertion == null) return false;
if (!assertion.getSystemPart().getEnabled()) return false;
}
if ((policy == null) || (principals == null)) {
return false;
}
// hard check to ensure an authenticated user for any modifications
// (regardless of configuration)
if (isWrite && !userWasAuthenticated) {
return false;
}
// check "any user" user privilege
if (principals.contains(AsnConstants.PRINCIPAL_ANY)) {
return true;
}
// check administrator privilege
if (userWasAuthenticated) {
if (principals.contains(AsnConstants.PRINCIPAL_ADMINISTRATOR)) {
RoleSet roles = user.getAuthenticationStatus().getAuthenticatedRoles();
if (roles.hasRole("gptAdministrator")) {
return true;
}
}
}
// check for ownership
if (userWasAuthenticated && (assertion != null) && assertion.getWasReadFromIndex()) {
if (principals.contains(AsnConstants.PRINCIPAL_OWNER)) {
String asnUserKey = Val.chkStr(assertion.getUserPart().getKey());
String userKey = Val.chkStr(user.getKey());
if ((asnUserKey.length() > 0) && asnUserKey.equals(userKey)) {
return true;
}
}
}
return false;
}
/**
* Determines if the user can create an assertion.
* @param context the assertion operation context
* @param policy the authorization policy
* @return true if the user can perform the action
*/
public boolean canCreate(AsnContext context, AsnAuthPolicy policy) {
return this.canAct(context,policy,null,AsnAuthorizer.ACTION_CREATE);
}
/**
* Determines if a the action user can delete an assertion.
* @param context the assertion operation context
* @param policy the authorization policy
* @param assertion the active assertion
* @return true if the user can perform the action
*/
public boolean canDelete(AsnContext context, AsnAuthPolicy policy, Assertion assertion) {
return this.canAct(context,policy,assertion,AsnAuthorizer.ACTION_DELETE);
}
/**
* Determines if a the action user can disable an assertion.
* @param context the assertion operation context
* @param policy the authorization policy
* @param assertion the active assertion
* @return true if the user can perform the action
*/
public boolean canDisable(AsnContext context, AsnAuthPolicy policy, Assertion assertion) {
return this.canAct(context,policy,assertion,AsnAuthorizer.ACTION_DISABLE);
}
/**
* Determines if a the action user can create an assertion.
* @param context the assertion operation context
* @param policy the authorization policy
* @param assertion the active assertion
* @return true if the user can perform the action
*/
public boolean canEnable(AsnContext context, AsnAuthPolicy policy, Assertion assertion) {
return this.canAct(context,policy,assertion,AsnAuthorizer.ACTION_ENABLE);
}
/**
* Determines if the user can query assertions.
* @param context the assertion operation context
* @param policy the authorization policy
* @return true if the user can perform the action
*/
public boolean canQuery(AsnContext context, AsnAuthPolicy policy) {
return this.canAct(context,policy,null,AsnAuthorizer.ACTION_QUERY);
}
/**
* Determines if a the action user can update an assertion.
* @param context the assertion operation context
* @param policy the authorization policy
* @param assertion the active assertion
* @return true if the user can perform the action
*/
public boolean canUpdate(AsnContext context, AsnAuthPolicy policy, Assertion assertion) {
return this.canAct(context,policy,assertion,AsnAuthorizer.ACTION_UPDATE);
}
/**
* Establishes the user associated with the operation.
* @param context the assertion operation context
* @throws NotAuthorizedException if authentication was required
* @throws AsnInsufficientPrivilegeException if the user has insufficient privilege
* @throws AsnUnestablishedUserException if the user could not be established
*/
public void establishUser(AsnContext context)
throws NotAuthorizedException, AsnUnestablishedUserException {
// initialize
this.setWasUserEstablished(false);
AsnOperation operation = context.getOperation();
User user = context.getRequestContext().getUser();
// establish the user part of the operation
if (operation.getUserPart() == null) {
operation.setUserPart(new AsnUserPart());
}
operation.getUserPart().setIPAddress(context.getRequestOptions().getIPAddress());
AsnAuthPolicy authPolicy = operation.getAuthPolicy();
if (authPolicy.getAuthenticationRequired()) {
if ((user == null) || !user.getAuthenticationStatus().getWasAuthenticated()) {
throw new NotAuthorizedException("Not authorized.");
}
}
if ((user == null) || !user.getAuthenticationStatus().getWasAuthenticated()) {
operation.getUserPart().setName(AsnConstants.ANONYMOUS_USERNAME);
this.setWasUserEstablished(true);
} else {
String key = Val.chkStr(user.getKey());
if (key.length() > 0) {
operation.getUserPart().setKey(key);
if (user.getLocalID() >= 0) {
operation.getUserPart().setID(""+user.getLocalID());
String name = Val.chkStr(user.getName());
if (name.length() > 0) {
operation.getUserPart().setName(name);
this.setWasUserEstablished(true);
}
}
}
}
if (!this.getWasUserEstablished()) {
throw new AsnUnestablishedUserException();
}
// check the admin database for a disabled user:ipaddress or user:key
// check the admin index for moderation privileges
}
}