package com.venky.swf.db.model;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.venky.cache.Cache;
import com.venky.core.collections.SequenceSet;
import com.venky.core.log.SWFLogger;
import com.venky.core.log.TimerStatistics.Timer;
import com.venky.core.util.ObjectUtil;
import com.venky.digest.Encryptor;
import com.venky.extension.Registry;
import com.venky.swf.db.Database;
import com.venky.swf.db.annotations.column.pm.PARTICIPANT;
import com.venky.swf.db.model.reflection.ModelReflector;
import com.venky.swf.db.table.ModelImpl;
import com.venky.swf.db.table.Table.ColumnDescriptor;
import com.venky.swf.exceptions.AccessDeniedException;
import com.venky.swf.pm.DataSecurityFilter;
import com.venky.swf.routing.Config;
import com.venky.swf.sql.Conjunction;
import com.venky.swf.sql.Expression;
import com.venky.swf.sql.Operator;
import com.venky.swf.sql.Select;
public class UserImpl extends ModelImpl<User>{
public UserImpl(User user) {
super(user);
}
public void generateApiKey(){
StringBuilder key = new StringBuilder();
key.append(getProxy().getId()).append(":").append(getProxy().getName()).append(":").append(getProxy().getPassword()).append(":").append(System.currentTimeMillis());
String encryptedKey = Encryptor.encrypt(key.toString());
getProxy().setApiKey(encryptedKey);
getProxy().save();
}
public String getChangePassword(){
return "";
}
public void setChangePassword(String password){
if (!ObjectUtil.isVoid(password)){
getProxy().setPassword(password);
}
}
public boolean authenticate(String password){
boolean ret = true;
try {
User user = getProxy();
if (Registry.instance().hasExtensions(User.USER_AUTHENTICATE)){
Registry.instance().callExtensions(User.USER_AUTHENTICATE, user,password);
}else {
ret = ObjectUtil.equals(user.getPassword(),password);
if (!ret){
Config.instance().getLogger(getClass().getName()).fine("Password mismatch '" + password + "' <> '" + user.getPassword() + "'");
}
}
}catch (AccessDeniedException ex){
ret = false;
}
return ret;
}
private Cache<Class<? extends Model>, SequenceSet<String>> participantExtensionPointsCache = new Cache<Class<? extends Model>, SequenceSet<String>>() {
/**
*
*/
private static final long serialVersionUID = -1863284571543640448L;
@Override
protected SequenceSet<String> getValue(Class<? extends Model> modelClass) {
SequenceSet<String> extnPoints = new SequenceSet<String>();
ModelReflector<? extends Model> ref = ModelReflector.instance(modelClass);
for (Class<? extends Model> inHierarchy : ref.getClassHierarchies()){
String extnPoint = User.GET_PARTICIPATION_OPTION + "."+ inHierarchy.getSimpleName();
extnPoints.add(extnPoint);
}
return extnPoints;
}
};
public <R extends Model> SequenceSet<String> getParticipationExtensionPoints(Class<R> modelClass){
return participantExtensionPointsCache.get(modelClass);
}
private Cache<Class<? extends Model>, Set<String>> relatedTables = new Cache<Class<? extends Model>, Set<String>>() {
private static final long serialVersionUID = 3264144367149148150L;
@Override
protected Set<String> getValue(Class<? extends Model> modelClass) {
Set<String> tables = new HashSet<String>();
load(tables,modelClass);
return tables;
}
private void load(Set<String> tables, Class<? extends Model> modelClass){
if (tables.add(ModelReflector.instance(modelClass).getTableName())){
for (Method m : ModelReflector.instance(modelClass).getParticipantModelGetters()){
@SuppressWarnings("unchecked")
Class<? extends Model> referredModelClass = (Class<? extends Model>)m.getReturnType();
load(tables,referredModelClass);
}
}
}
};
private final SWFLogger cat = Config.instance().getLogger(getClass().getName());
public <R extends Model> Cache<String,Map<String,List<Integer>>> getParticipationOptions(Class<R> modelClass){
Timer timer = cat.startTimer("getting participating Options for " + modelClass.getSimpleName());
Set<String> tables = new HashSet<String>(relatedTables.get(modelClass));
tables.retainAll(Database.getInstance().getCurrentTransaction().getTablesChanged());
Cache<Class<? extends Model>,Cache<String,Map<String,List<Integer>>>> baseParticipationOptions = Database.getInstance().getCurrentTransaction().getAttribute(this.getClass().getName() + ".getParticipationOptions" );
if (baseParticipationOptions == null){
baseParticipationOptions = new Cache<Class<? extends Model>, Cache<String,Map<String,List<Integer>>>>() {
private static final long serialVersionUID = -5570691940356510299L;
@Override
protected Cache<String, Map<String, List<Integer>>> getValue(
Class<? extends Model> k) {
return getParticipationOptions(k,Database.getTable(k).newRecord());
}
};
Database.getInstance().getCurrentTransaction().setAttribute(this.getClass().getName() + ".getParticipationOptions" ,baseParticipationOptions);
}
if (!tables.isEmpty()){
baseParticipationOptions.remove(modelClass);
}
timer.stop();
return baseParticipationOptions.get(modelClass);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Cache<String,Map<String,List<Integer>>> getParticipationOptions(Class<? extends Model> modelClass, Model model){
Timer timer = cat.startTimer("getting participating Options for " + modelClass.getSimpleName() +"/" + (model != null ? model.getId() : "" ));
try {
Cache<String,Map<String, List<Integer>>> mapParticipatingGroupOptions = new Cache<String, Map<String,List<Integer>>>(){
/**
*
*/
private static final long serialVersionUID = -6588079568930541478L;
@Override
protected Map<String, List<Integer>> getValue(String k) {
return new HashMap<String, List<Integer>>();
}
};
User user = getProxy();
if (user.isAdmin()){
return mapParticipatingGroupOptions;
}
ModelReflector<? extends Model> ref = ModelReflector.instance(modelClass);
for (Method referredModelGetter : ref.getParticipantModelGetters()){
String referredModelIdFieldName = ref.getReferenceField(referredModelGetter);
PARTICIPANT participant = ref.getAnnotation(ref.getFieldGetter(referredModelIdFieldName), PARTICIPANT.class);
Map<String,List<Integer>> mapParticipatingOptions = mapParticipatingGroupOptions.get(participant.value());
boolean extnFound = false;
for (String extnPoint: getParticipationExtensionPoints(modelClass)){
if (Registry.instance().hasExtensions(extnPoint)){
Registry.instance().callExtensions(extnPoint, user, model,referredModelIdFieldName, mapParticipatingGroupOptions);
extnFound = true;
break;
}
}
if (!extnFound && Registry.instance().hasExtensions(User.GET_PARTICIPATION_OPTION)){
Registry.instance().callExtensions(User.GET_PARTICIPATION_OPTION, user, modelClass, model, referredModelIdFieldName, mapParticipatingGroupOptions);
extnFound = true;
}
if (!extnFound) {
Class<? extends Model> referredModelClass = (Class<? extends Model>) referredModelGetter.getReturnType();
Integer rmid = ref.get(model,referredModelIdFieldName);
Model referredModel = null;
if (rmid != null){
referredModel = Database.getTable(referredModelClass).get(rmid);
}
Cache<String,Map<String,List<Integer>>> referredModelParticipatingGroupOptions = null;
if (referredModel == null){
referredModelParticipatingGroupOptions = user.getParticipationOptions(referredModelClass);
}else {
referredModelParticipatingGroupOptions = user.getParticipationOptions(referredModelClass,referredModel);
}
ModelReflector<?> referredModelReflector = ModelReflector.instance(referredModelClass);
if (referredModelParticipatingGroupOptions.size() > 0){
Set<String> fields = new HashSet<String>();
for (String g: referredModelParticipatingGroupOptions.keySet()){
fields.addAll(referredModelParticipatingGroupOptions.get(g).keySet());
}
fields.removeAll(DataSecurityFilter.getRedundantParticipationFields(fields, referredModelReflector));
boolean couldFilterUsingDSW = !DataSecurityFilter.anyFieldIsVirtual(fields,referredModelReflector);
Select q = new Select().from(referredModelClass);
Select.ResultFilter filter = null;
if (couldFilterUsingDSW){
q.where(getDataSecurityWhereClause(referredModelReflector,referredModelParticipatingGroupOptions));
}else {
filter = new Select.AccessibilityFilter<Model>(user);
}
List<? extends Model> referables = q.execute(referredModelClass,filter);
List<Integer> ids = DataSecurityFilter.getIds(referables);
mapParticipatingOptions.put(referredModelIdFieldName,ids);
}
}
}
return mapParticipatingGroupOptions;
}finally{
timer.stop();
}
}
public boolean isAdmin(){
return getProxy().getId() == 1;
}
public Expression getDataSecurityWhereClause(Class<? extends Model> modelClass){
Model dummy = Database.getTable(modelClass).newRecord();
return getDataSecurityWhereClause(modelClass, dummy);
}
public Expression getDataSecurityWhereClause(Class<? extends Model> modelClass,Model model){
ModelReflector<? extends Model> ref = ModelReflector.instance(modelClass);
Cache<String,Map<String,List<Integer>>> participatingOptions = getParticipationOptions(modelClass,model);
return getDataSecurityWhereClause(ref, participatingOptions);
}
public Expression getDataSecurityWhereClause(ModelReflector<? extends Model> ref, Cache<String,Map<String,List<Integer>>> participatingGroupOptions){
Expression dswMandatory = new Expression(getPool(),Conjunction.AND);
Map<String,Expression> optionalWhere = new HashMap<String, Expression>();
for (String participantRoleGroup: participatingGroupOptions.keySet()){
Expression dswOptional = new Expression(getPool(),Conjunction.OR);
optionalWhere.put(participantRoleGroup, dswOptional);
dswMandatory.add(dswOptional);
}
for (String participantRoleGroup : participatingGroupOptions.keySet()){
Map<String,List<Integer>> participatingOptions = participatingGroupOptions.get(participantRoleGroup);
Iterator<String> fieldNameIterator = participatingOptions.keySet().iterator();
while (fieldNameIterator.hasNext()){
String key = fieldNameIterator.next();
List<Integer> values = participatingOptions.get(key);
ColumnDescriptor cd = ref.getColumnDescriptor(key);
if (cd.isVirtual()){
continue;
}
Expression dsw = optionalWhere.get(participantRoleGroup);
if (values.isEmpty()){
dsw.add(new Expression(getPool(),cd.getName(),Operator.EQ));
}else if (values.size() == 1){
Integer value = values.get(0);
if (value == null){
dsw.add(new Expression(getPool(),cd.getName(),Operator.EQ));
}else {
dsw.add(new Expression(getPool(),cd.getName(),Operator.EQ, values.get(0)));
}
}else {
dsw.add(Expression.createExpression(getPool(),cd.getName(),Operator.IN, values.toArray()));
}
}
}
return dswMandatory;
}
public User getSelfUser(){
return getProxy();
}
}