/*******************************************************************************
*
* Copyright 2010 Alexandru Craciun, and individual contributors as indicated
* by the @authors tag.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
******************************************************************************/
package org.netxilia.api.impl.user;
import java.security.AccessControlException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.netxilia.api.INetxiliaSystem;
import org.netxilia.api.command.CellCommands;
import org.netxilia.api.exception.AlreadyExistsException;
import org.netxilia.api.exception.NetxiliaBusinessException;
import org.netxilia.api.exception.NetxiliaResourceException;
import org.netxilia.api.exception.NotFoundException;
import org.netxilia.api.exception.StorageException;
import org.netxilia.api.formula.Formula;
import org.netxilia.api.impl.model.SheetNames;
import org.netxilia.api.model.CellData;
import org.netxilia.api.model.ISheet;
import org.netxilia.api.model.SheetFullName;
import org.netxilia.api.model.SheetType;
import org.netxilia.api.model.WorkbookId;
import org.netxilia.api.operation.ISheetOperations;
import org.netxilia.api.reference.AreaReference;
import org.netxilia.api.user.AclObjectType;
import org.netxilia.api.user.AclPrivilegedMode;
import org.netxilia.api.user.IAclService;
import org.netxilia.api.user.IUserService;
import org.netxilia.api.user.Permission;
import org.netxilia.api.user.User;
import org.netxilia.api.utils.Matrix;
import org.netxilia.api.value.IGenericValue;
import org.netxilia.api.value.StringValue;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* @author <a href='mailto:ax.craciun@gmail.com'>Alexandru Craciun</a>
*
*/
public class AclServiceImpl implements IAclService {
private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(AclServiceImpl.class);
private String permissionSheet = SheetNames.PERMISSIONS;
private static final Map<String, IGenericValue> HEADERS = new HashMap<String, IGenericValue>();
private static final String ANY_USER = "*";
private static final String ANY_SHEET = "*";
static {
HEADERS.put("A", new StringValue("Object type"));
HEADERS.put("B", new StringValue("User"));
HEADERS.put("C", new StringValue("Object name"));
HEADERS.put("D", new StringValue("Permission"));
}
@Autowired
private INetxiliaSystem workbookProcessor;
@Autowired
private IUserService userService;
@Autowired
private ISheetOperations sheetOperations;
public INetxiliaSystem getWorkbookProcessor() {
return workbookProcessor;
}
public void setWorkbookProcessor(INetxiliaSystem workbookProcessor) {
this.workbookProcessor = workbookProcessor;
}
public IUserService getUserService() {
return userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
public ISheetOperations getSheetOperations() {
return sheetOperations;
}
public void setSheetOperations(ISheetOperations sheetOperations) {
this.sheetOperations = sheetOperations;
}
protected ISheet getAclSheet(WorkbookId workbookId) throws StorageException, NotFoundException {
try {
return workbookProcessor.getWorkbook(workbookId).getSheet(permissionSheet);
} catch (NotFoundException e) {
ISheet aclSheet = null;
try {
aclSheet = workbookProcessor.getWorkbook(workbookId).addNewSheet(permissionSheet, SheetType.normal);
} catch (AlreadyExistsException e1) {
// nothing to do
}
aclSheet.sendCommand(CellCommands.mapValues(AreaReference.lastRow(0, 3), HEADERS));
return aclSheet;
}
}
private boolean checkPermission(INetxiliaSystem workbookProcessor, ISheet aclSheet, AclObjectType objectType,
String userSpec, String sheetName, Permission permission) throws NetxiliaResourceException,
NetxiliaBusinessException {
// A=object type => sheet
// B=username
// C=object name => sheet name
// D=permissions comma delimited
List<Integer> rows;
String formulaText = "=AND(A1=\"" + objectType.name() + "\", B1=\"" + userSpec + "\"";
if (sheetName != null) {
formulaText += ",C1=\"" + sheetName + "\"";
}
formulaText += ")";
rows = sheetOperations.filter(aclSheet, new Formula(formulaText)).getNonBlocking();
// NO AND YET
if (rows == null || rows.size() == 0) {
return false;
}
int row = rows.get(0);
Matrix<CellData> cells = aclSheet.receiveCells(new AreaReference(null, row, 0, row, 3)).getNonBlocking();
String permissions = cells.get(0, 3).getValue().getStringValue();
String[] permissionsArray = StringUtils.split(permissions, ",");
for (int i = 0; i < permissionsArray.length; ++i) {
Permission p = Permission.valueOf(permissionsArray[i]);
if (p == permission) {
return true;
}
}
return false;
}
@Override
public void checkPermission(SheetFullName sheetFullName, Permission permission) throws AccessControlException {
if (log.isDebugEnabled()) {
log.debug("Check for " + sheetFullName + " " + permission + " isSet:" + AclPrivilegedMode.isSet());
}
if (AclPrivilegedMode.isSet()) {
return;
}
ISheet aclSheet = null;
boolean wasSet = AclPrivilegedMode.set();
try {
aclSheet = getAclSheet(new WorkbookId(sheetFullName.getWorkbookName()));
User user = userService.getCurrentUser();
if (user == null) {
throw new AccessControlException("No current user");
}
if (user.isAdmin()) {
return;
}
// sheet.summary has the same permissions as sheet itself
String sheetName = SheetFullName.sheetSimpleName(sheetFullName.getSheetName(), user);
// if it's the user's private sheet, all access is allowed
if (sheetName.equals(SheetFullName.privateSheetName(sheetFullName, user))) {
return;
}
// check user
if (checkPermission(workbookProcessor, aclSheet, AclObjectType.sheet, user.getLogin(), sheetName,
permission)) {
return;
}
// TODO: check groups
// check all
if (checkPermission(workbookProcessor, aclSheet, AclObjectType.sheet, ANY_USER, sheetName, permission)) {
return;
}
if (checkPermission(workbookProcessor, aclSheet, AclObjectType.sheet, user.getLogin(), ANY_SHEET,
permission)) {
return;
}
throw new AccessControlException("Operation not permitted");
} catch (NotFoundException e) {
// only happens if somebody deleted the sheet right before the filtering
throw new AccessControlException("Cannot check permissions. Reason: " + e);
} catch (NetxiliaResourceException e) {
throw new AccessControlException("Cannot check permissions. Reason: " + e);
} catch (NetxiliaBusinessException e) {
throw new AccessControlException("Cannot check permissions. Reason: " + e);
} finally {
if (!wasSet) {
AclPrivilegedMode.clear();
}
if (log.isDebugEnabled()) {
log.debug("<-- done for " + sheetFullName + " " + permission);
}
}
}
@Override
public void setPermissions(SheetFullName sheetFullName, User grantee, Permission... permissions)
throws AccessControlException, StorageException {
// sheet.summary has the same permissions as sheet itself
if (sheetFullName.getType() != SheetType.normal) {
return;
}
setPermissions(AclObjectType.sheet, new WorkbookId(sheetFullName.getWorkbookName()),
sheetFullName.getSheetName(), grantee, permissions);
}
@Override
public void checkPermission(WorkbookId workbookId, Permission permission) throws AccessControlException {
if (log.isDebugEnabled()) {
log.debug("Check for " + workbookId + " " + permission + " isSet:" + AclPrivilegedMode.isSet());
}
if (AclPrivilegedMode.isSet()) {
return;
}
ISheet aclSheet = null;
boolean wasSet = AclPrivilegedMode.set();
try {
aclSheet = getAclSheet(workbookId);
User user = userService.getCurrentUser();
if (user == null) {
throw new AccessControlException("No current user");
}
if (user.isAdmin()) {
return;
}
// check user
if (checkPermission(workbookProcessor, aclSheet, AclObjectType.workbook, user.getLogin(), null, permission)) {
return;
}
// TODO: check groups
// check all
if (checkPermission(workbookProcessor, aclSheet, AclObjectType.workbook, ANY_USER, null, permission)) {
return;
}
throw new AccessControlException("Operation not permitted");
} catch (NotFoundException e) {
// only happens if somebody deleted the sheet right before the filtering
throw new AccessControlException("Cannot check permissions. Reason: " + e);
} catch (NetxiliaResourceException e) {
throw new AccessControlException("Cannot check permissions. Reason: " + e);
} catch (NetxiliaBusinessException e) {
throw new AccessControlException("Cannot check permissions. Reason: " + e);
} finally {
if (!wasSet) {
AclPrivilegedMode.clear();
}
if (log.isDebugEnabled()) {
log.debug("<-- done for " + workbookId + " " + permission);
}
}
}
@Override
public void setPermissions(WorkbookId id, User grantee, Permission... permissions) throws AccessControlException,
StorageException {
setPermissions(AclObjectType.workbook, id, null, grantee, permissions);
}
private void setPermissions(AclObjectType objectType, WorkbookId workbookId, String sheetName, User grantee,
Permission... permissions) throws StorageException {
// A=object type => sheet
// B=username
// C=object name => sheet name
// D=permissions comma delimited
ISheet aclSheet = null;
boolean wasSet = AclPrivilegedMode.set();
try {
aclSheet = getAclSheet(workbookId);
User user = userService.getCurrentUser();
if (user == null) {
throw new AccessControlException("No current user");
}
if (user.isAdmin()) {
return;
}
Map<String, IGenericValue> row = new HashMap<String, IGenericValue>();
row.put("A", new StringValue(objectType.name()));
row.put("B", new StringValue(user.getLogin()));
row.put("C", new StringValue(sheetName));
StringBuilder permString = new StringBuilder();
for (Permission perm : permissions) {
if (permString.length() > 0) {
permString.append(",");
}
permString.append(perm.name());
}
row.put("D", new StringValue(permString.toString()));
aclSheet.sendCommand(CellCommands.mapValues(AreaReference.lastRow(0, 3), row));
} catch (NotFoundException e) {
// only happens if somebody deleted the sheet right before the filtering
throw new StorageException(e);
} finally {
if (!wasSet) {
AclPrivilegedMode.clear();
}
}
}
}