/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */
package foam.nanos.auth;

import foam.core.X;
import foam.core.XFactory;
import foam.nanos.session.Session;
import java.security.Permission;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import javax.security.auth.AuthPermission;


/** Only return value if the session context hasn't changed. **/
class SessionContextCacheFactory
  implements XFactory
{
  protected X      sessionX_;
  protected Object value_;

  public SessionContextCacheFactory(Object value) {
    value_ = value;
  }

  public Object create(X x) {
    Session session = (Session) x.get(Session.class);
    if ( session == null ) return null;
    if ( sessionX_ == null ) sessionX_ = session.getContext();
    if ( sessionX_ != session.getContext() ) return null;
    return value_;
  }
}


/**
 * Decorator to add Caching to AuthService.
 * Stores cache in user Session so that memory is freed when user logs out.
 **/
public class CachingAuthService
  extends ProxyAuthService
{

  public static String CACHE_KEY = "CachingAuthService.PermissionCache";

  protected static Map<String,Boolean> getPermissionMap(X x) {
    Session session = (Session) x.get(Session.class);
    Map<String,Boolean> map = (Map) session.getContext().get(CACHE_KEY);

    if ( map == null ) {
      map = new ConcurrentHashMap<String,Boolean>();
      session.setContext(session.getContext().putFactory(
        CACHE_KEY,
        new SessionContextCacheFactory(map)));
    }

    return map;
  }

  public static void purgeCache(X x) {
    getPermissionMap(x).clear();
  }

  public CachingAuthService(AuthService delegate) {
    setDelegate(delegate);
  }

  @Override
  public boolean check(foam.core.X x, String permission) {
    if ( x == null || permission == null ) return false;

    Permission p = new AuthPermission(permission);

    Map<String,Boolean> map = getPermissionMap(x);

    if ( map.containsKey(p.getName()) ) {
      return map.get(p.getName());
    }

    boolean permissionCheck = getDelegate().check(x, permission);

    map.put(p.getName(), permissionCheck);

    return permissionCheck;
  }
}

@@ -163,36 +163,108 @@
 ore.
-X;%0Aimport foam.core.XFactory
+Detachable;%0Aimport foam.core.X;%0Aimport foam.core.XFactory;%0Aimport foam.dao.DAO;%0Aimport foam.dao.Sink
 ;%0Aim
@@ -441,16 +441,52 @@
 ission;%0A
+import static foam.mlang.MLang.TRUE;
 %0A%0A/** On
@@ -1329,16 +1329,22 @@
 sionMap(
+final 
 X x) %7B%0A 
@@ -1346,32 +1346,44 @@
 ) %7B%0A    Session 
+            
 session = (Sessi
@@ -1436,16 +1436,20 @@
 an%3E map 
+    
 = (Map) 
@@ -1507,24 +1507,409 @@
 == null ) %7B%0A
+      DAO dao = (DAO) x.get(%22groupDAO%22);%0A      dao.listen(new Sink() %7B%0A        public void put(Object obj, Detachable sub) %7B%0A          purgeCache(x);%0A        %7D%0A        public void remove(Object obj, Detachable sub) %7B%0A          purgeCache(x);%0A        %7D%0A        public void eof() %7B%0A        %7D%0A        public void reset(Detachable sub) %7B%0A          purgeCache(x);%0A        %7D%0A      %7D, TRUE);%0A
       map = 

Purge Auth cache if GroupDAO changes.