killbill-uncached
Changes
pom.xml 2(+1 -1)
profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillSessionStorageEvaluator.java 66(+66 -0)
profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java 23(+20 -3)
Details
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index 02d1b7e..35d1373 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill-oss-parent</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.142.6</version>
+ <version>0.142.7</version>
</parent>
<artifactId>killbill</artifactId>
<version>0.20.3-SNAPSHOT</version>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillSessionStorageEvaluator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillSessionStorageEvaluator.java
new file mode 100644
index 0000000..cce4626
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillSessionStorageEvaluator.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.server.modules;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.shiro.mgt.SessionStorageEvaluator;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.util.RequestPairSource;
+import org.killbill.billing.jaxrs.resources.JaxrsResource;
+
+public class KillBillSessionStorageEvaluator implements SessionStorageEvaluator {
+
+ @Override
+ public boolean isSessionStorageEnabled(final Subject subject) {
+ if (subject.getSession(false) != null) {
+ // Use what already exists
+ return true;
+ }
+
+ return isSessionCreationEnabled(subject);
+ }
+
+ private boolean isSessionCreationEnabled(final Subject requestPairSource) {
+ if (requestPairSource instanceof RequestPairSource) {
+ final RequestPairSource source = (RequestPairSource) requestPairSource;
+ return isSessionCreationEnabled(source.getServletRequest());
+ }
+ return false; // By default
+ }
+
+ private boolean isSessionCreationEnabled(final ServletRequest request) {
+ if (request != null) {
+ // Only create new sessions via the /1.0/kb/security/permissions endpoint, as this is what is used today
+ // by Kaui to initiate the session.
+ // If we have another use-case one day, we could think about introducing a proper 'login' endpoint...
+ return isPermissionsLookupCall(request);
+ }
+ return false; // By default
+ }
+
+ private boolean isPermissionsLookupCall(final ServletRequest request) {
+ if (request instanceof HttpServletRequest) {
+ final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ final String path = httpServletRequest.getPathInfo();
+ return (JaxrsResource.SECURITY_PATH + "/permissions").equals(path);
+ }
+ return false;
+ }
+}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
index 90b2144..b8a7992 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
@@ -31,8 +31,8 @@ import org.apache.shiro.authc.pam.ModularRealmAuthenticatorWith540;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.guice.web.ShiroWebModuleWith435;
+import org.apache.shiro.mgt.SubjectDAO;
import org.apache.shiro.realm.Realm;
-import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
@@ -41,6 +41,7 @@ import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.util.WebUtils;
import org.killbill.billing.jaxrs.resources.JaxrsResource;
import org.killbill.billing.server.security.FirstSuccessfulStrategyWith540;
+import org.killbill.billing.server.security.KillBillWebSessionManager;
import org.killbill.billing.server.security.KillbillJdbcTenantRealm;
import org.killbill.billing.util.config.definition.RbacConfig;
import org.killbill.billing.util.config.definition.RedisCacheConfig;
@@ -118,6 +119,7 @@ public class KillBillShiroWebModule extends ShiroWebModuleWith435 {
if (KillBillShiroModule.isRBACEnabled()) {
addFilterChain(JaxrsResource.PREFIX + "/**", Key.get(CorsBasicHttpAuthenticationFilter.class));
+ addFilterChain(JaxrsResource.PLUGINS_PATH + "/**", Key.get(CorsBasicHttpAuthenticationOptionalFilter.class));
}
}
@@ -131,13 +133,15 @@ public class KillBillShiroWebModule extends ShiroWebModuleWith435 {
protected void bindSessionManager(final AnnotatedBindingBuilder<SessionManager> bind) {
// Bypass the servlet container completely for session management and delegate it to Shiro.
// The default session timeout is 30 minutes.
- bind.to(DefaultSessionManager.class).asEagerSingleton();
+ bind.to(KillBillWebSessionManager.class).asEagerSingleton();
+
+ bind(SubjectDAO.class).toProvider(KillBillWebSubjectDAOProvider.class).asEagerSingleton();
// Magic provider to configure the session DAO
bind(SessionDAO.class).toProvider(SessionDAOProvider.class).asEagerSingleton();
}
- public static final class CorsBasicHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
+ public static class CorsBasicHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(final ServletRequest request, final ServletResponse response, final Object mappedValue) {
@@ -149,6 +153,19 @@ public class KillBillShiroWebModule extends ShiroWebModuleWith435 {
}
}
+ public static final class CorsBasicHttpAuthenticationOptionalFilter extends CorsBasicHttpAuthenticationFilter {
+
+ protected boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception {
+ if (isLoginAttempt(request, response)) {
+ // Attempt to log-in
+ executeLogin(request, response);
+ }
+
+ // Unlike the original method, we don't send a challenge on failure but simply allow the request to continue
+ return true;
+ }
+ }
+
private final class DefaultWebSecurityManagerTypeListener implements TypeListener {
@Override
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillWebSubjectDAOProvider.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillWebSubjectDAOProvider.java
new file mode 100644
index 0000000..f6a986e
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillWebSubjectDAOProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.server.modules;
+
+import org.apache.shiro.mgt.SubjectDAO;
+import org.killbill.billing.util.glue.KillBillSubjectDAO;
+
+import com.google.inject.Provider;
+
+public class KillBillWebSubjectDAOProvider implements Provider<SubjectDAO> {
+
+ @Override
+ public SubjectDAO get() {
+ final KillBillSubjectDAO killBillSubjectDAO = new KillBillSubjectDAO();
+ killBillSubjectDAO.setSessionStorageEvaluator(new KillBillSessionStorageEvaluator());
+ return killBillSubjectDAO;
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/glue/EhcacheShiroManagerProvider.java b/util/src/main/java/org/killbill/billing/util/glue/EhcacheShiroManagerProvider.java
index 72f355a..0d83057 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/EhcacheShiroManagerProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/EhcacheShiroManagerProvider.java
@@ -28,6 +28,7 @@ import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.mgt.SubjectDAO;
import org.ehcache.integrations.shiro.EhcacheShiro;
import org.ehcache.integrations.shiro.EhcacheShiroManager;
import org.killbill.billing.util.config.definition.EhCacheConfig;
@@ -39,16 +40,19 @@ import com.codahale.metrics.MetricRegistry;
public class EhcacheShiroManagerProvider extends EhCacheProviderBase implements Provider<EhcacheShiroManager> {
private final SecurityManager securityManager;
+ private final SubjectDAO subjectDAO;
private final CacheManager eh107CacheManager;
private final org.ehcache.CacheManager ehcacheCacheManager;
@Inject
public EhcacheShiroManagerProvider(final SecurityManager securityManager,
+ final SubjectDAO subjectDAO,
final CacheManager eh107CacheManager,
final MetricRegistry metricRegistry,
final EhCacheConfig cacheConfig) {
super(metricRegistry, cacheConfig);
this.securityManager = securityManager;
+ this.subjectDAO = subjectDAO;
this.eh107CacheManager = eh107CacheManager;
this.ehcacheCacheManager = getEhcacheManager();
}
@@ -63,7 +67,7 @@ public class EhcacheShiroManagerProvider extends EhCacheProviderBase implements
// For RBAC only (see also KillbillJdbcTenantRealmProvider)
final DefaultSecurityManager securityManager = (DefaultSecurityManager) this.securityManager;
securityManager.setCacheManager(shiroEhCacheManager);
- securityManager.setSubjectDAO(new KillBillSubjectDAO());
+ securityManager.setSubjectDAO(subjectDAO);
}
return shiroEhCacheManager;
diff --git a/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java b/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
index 420a565..2f9a3e0 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
@@ -21,6 +21,7 @@ package org.killbill.billing.util.glue;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.guice.ShiroModule;
import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.mgt.SubjectDAO;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionManager;
@@ -132,6 +133,8 @@ public class KillBillShiroModule extends ShiroModule {
protected void bindSessionManager(final AnnotatedBindingBuilder<SessionManager> bind) {
bind.to(DefaultSessionManager.class).asEagerSingleton();
+ bind(SubjectDAO.class).toProvider(KillBillSubjectDAOProvider.class).asEagerSingleton();
+
// Magic provider to configure the session DAO
bind(SessionDAO.class).toProvider(SessionDAOProvider.class).asEagerSingleton();
}
diff --git a/util/src/main/java/org/killbill/billing/util/glue/KillBillSubjectDAOProvider.java b/util/src/main/java/org/killbill/billing/util/glue/KillBillSubjectDAOProvider.java
new file mode 100644
index 0000000..7efa569
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/glue/KillBillSubjectDAOProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.glue;
+
+import org.apache.shiro.mgt.SessionStorageEvaluator;
+import org.apache.shiro.mgt.SubjectDAO;
+import org.apache.shiro.subject.Subject;
+
+import com.google.inject.Provider;
+
+// See org.killbill.billing.server.modules.KillBillWebSubjectDAOProvider for the web version
+public class KillBillSubjectDAOProvider implements Provider<SubjectDAO> {
+
+ @Override
+ public SubjectDAO get() {
+ final KillBillSubjectDAO killBillSubjectDAO = new KillBillSubjectDAO();
+ killBillSubjectDAO.setSessionStorageEvaluator(new SessionStorageEvaluator() {
+ @Override
+ public boolean isSessionStorageEnabled(final Subject subject) {
+ // Use what already exists
+ return subject.getSession(false) != null;
+ }
+ });
+ return killBillSubjectDAO;
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/glue/RedisShiroManagerProvider.java b/util/src/main/java/org/killbill/billing/util/glue/RedisShiroManagerProvider.java
index bcfa133..e992feb 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/RedisShiroManagerProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/RedisShiroManagerProvider.java
@@ -23,6 +23,7 @@ import javax.inject.Provider;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.mgt.SubjectDAO;
import org.redisson.api.RedissonClient;
import com.codahale.metrics.MetricRegistry;
@@ -32,17 +33,20 @@ import static org.killbill.billing.util.glue.CacheModule.REDIS_CACHE_CLIENT;
public class RedisShiroManagerProvider implements Provider<RedisShiroManager> {
- private final CacheManager eh107CacheManager;
private final SecurityManager securityManager;
+ private final SubjectDAO subjectDAO;
+ private final CacheManager eh107CacheManager;
private final MetricRegistry metricRegistry;
private final RedissonClient redissonClient;
@Inject
public RedisShiroManagerProvider(final SecurityManager securityManager,
+ final SubjectDAO subjectDAO,
final CacheManager eh107CacheManager,
final MetricRegistry metricRegistry,
@Named(REDIS_CACHE_CLIENT) final RedissonClient redissonClient) {
this.securityManager = securityManager;
+ this.subjectDAO = subjectDAO;
this.eh107CacheManager = eh107CacheManager;
this.metricRegistry = metricRegistry;
this.redissonClient = redissonClient;
@@ -57,7 +61,7 @@ public class RedisShiroManagerProvider implements Provider<RedisShiroManager> {
// For RBAC only (see also KillbillJdbcTenantRealmProvider)
final DefaultSecurityManager securityManager = (DefaultSecurityManager) this.securityManager;
securityManager.setCacheManager(shiroRedisManager);
- securityManager.setSubjectDAO(new KillBillSubjectDAO());
+ securityManager.setSubjectDAO(subjectDAO);
}
return shiroRedisManager;
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
index 8bd91cc..6e455b8 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
@@ -20,6 +20,9 @@ package org.killbill.billing.util.security.shiro.dao;
import java.io.IOException;
import java.io.Serializable;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -90,12 +93,20 @@ public class JDBCSessionDao extends CachingSessionDAO {
return null;
}
- try {
- return sessionModelDao.toSimpleSession();
- } catch (final IOException e) {
- log.warn("Corrupted cookie", e);
- return null;
+ return toSession(sessionModelDao);
+ }
+
+ @Override
+ public Collection<Session> getActiveSessions() {
+ final Collection<Session> cachedActiveSessions = super.getActiveSessions();
+ // To make sure the ValidatingSessionManager purges old sessions on disk
+ final List<SessionModelDao> oldActiveSessionsOnDisk = dbRouter.onDemand(true).findOldActiveSessions();
+
+ final Collection<Session> activeSessions = new LinkedList<Session>(cachedActiveSessions);
+ for (final SessionModelDao sessionModelDao : oldActiveSessionsOnDisk) {
+ activeSessions.add(toSession(sessionModelDao));
}
+ return activeSessions;
}
public void disableUpdatesForSession(final Session session) {
@@ -110,4 +121,13 @@ public class JDBCSessionDao extends CachingSessionDAO {
private boolean shouldUpdateSession(final Session session) {
return noUpdateSessionsCache.getIfPresent(session.getId()) == Boolean.TRUE ? Boolean.FALSE : Boolean.TRUE;
}
+
+ private Session toSession(final SessionModelDao sessionModelDao) {
+ try {
+ return sessionModelDao.toSimpleSession();
+ } catch (final IOException e) {
+ log.warn("Corrupted cookie", e);
+ return null;
+ }
+ }
}
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
index 409f2a3..d78e6c1 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
@@ -18,8 +18,10 @@
package org.killbill.billing.util.security.shiro.dao;
-import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
+import java.util.List;
+
import org.killbill.commons.jdbi.binder.SmartBindBean;
+import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -40,4 +42,6 @@ public interface JDBCSessionSqlDao extends Transactional<JDBCSessionSqlDao> {
@SqlUpdate
public void delete(@SmartBindBean final SessionModelDao sessionModelDao);
+ @SqlQuery
+ public List<SessionModelDao> findOldActiveSessions();
}
diff --git a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
index e6091e5..c479317 100644
--- a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
+++ b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
@@ -45,4 +45,19 @@ delete() ::= <<
delete from sessions
where id = :id
;
->>
\ No newline at end of file
+>>
+
+findOldActiveSessions() ::= <<
+select
+ record_id
+, id
+, start_timestamp
+, last_access_time
+, timeout
+, host
+, session_data
+from sessions
+order by record_id asc
+limit 100
+;
+>>