Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixing some bad line endings that I introduced.

  • Loading branch information...
commit 3d9538db0f90f747fb057812c1a5b1a61989b976 1 parent 62cbfdc
@sronderos sronderos authored
View
402 LICENSE.txt
@@ -1,201 +1,201 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed 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.
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
View
32 src/docs/guide/4.2. mt-spring-security.gdoc
@@ -1,17 +1,17 @@
-This script is intended to help users integrate multi-tenant-single-db with spring-security-core.
-
-More specifically, this will generate the required multi-tenant-single-db classes for you to support multiple Spring Security users within a single tenant.
-
-This will not help you if you intend to mark the Spring Security class with the @MultiTenant@ annotation (these classes use the User domain to store the multi-tenant id.
-
-This script will generate:
-
-# A tenant resolver that integrates with Spring Securty
-# Domain class to represent tenants
-# A tenant repository implementation that integrates with Spring Security
-
-You will have to update @grails-app/conf/Config.groovy@ and @grails-app/conf/spring/resources.groovy@ after running this script.
-
-{code}
-Example: grails mt-spring-security com.yourapp Customer
+This script is intended to help users integrate multi-tenant-single-db with spring-security-core.
+
+More specifically, this will generate the required multi-tenant-single-db classes for you to support multiple Spring Security users within a single tenant.
+
+This will not help you if you intend to mark the Spring Security class with the @MultiTenant@ annotation (these classes use the User domain to store the multi-tenant id.
+
+This script will generate:
+
+# A tenant resolver that integrates with Spring Securty
+# Domain class to represent tenants
+# A tenant repository implementation that integrates with Spring Security
+
+You will have to update @grails-app/conf/Config.groovy@ and @grails-app/conf/spring/resources.groovy@ after running this script.
+
+{code}
+Example: grails mt-spring-security com.yourapp Customer
{code}
View
346 src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListener.java
@@ -1,174 +1,174 @@
-package grails.plugin.multitenant.singledb.hibernate;
-
-import grails.plugin.hibernatehijacker.hibernate.events.HibernateEventPropertyUpdater;
-import grails.plugin.multitenant.core.CurrentTenant;
-import grails.plugin.multitenant.core.MultiTenantDomainClass;
-import grails.plugin.multitenant.core.ast.MultiTenantAST;
-import grails.plugin.multitenant.core.exception.NoCurrentTenantException;
-import grails.plugin.multitenant.core.exception.TenantSecurityException;
-
-import org.hibernate.HibernateException;
-import org.hibernate.event.LoadEvent;
-import org.hibernate.event.LoadEventListener;
-import org.hibernate.event.PostLoadEvent;
-import org.hibernate.event.PostLoadEventListener;
-import org.hibernate.event.PreDeleteEvent;
-import org.hibernate.event.PreDeleteEventListener;
-import org.hibernate.event.PreInsertEvent;
-import org.hibernate.event.PreInsertEventListener;
-import org.hibernate.event.PreUpdateEvent;
-import org.hibernate.event.PreUpdateEventListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Listens for pre-insert, pre-update and load / fetch Hibernate events. If the
- * domain class related to the event is a multi-tenant class we apply the
- * relevant constraints.
- *
- * @author Kim A. Betti
- */
-@SuppressWarnings("serial")
-public class TenantHibernateEventListener implements PreInsertEventListener, PreUpdateEventListener, LoadEventListener, PostLoadEventListener, PreDeleteEventListener {
-
- private static Logger log = LoggerFactory.getLogger(TenantHibernateFilterConfigurator.class);
-
- private CurrentTenant currentTenant;
- private HibernateEventPropertyUpdater hibernateEventPropertyUpdater;
-
- /**
- * One important thing to know. It's not enough to update the entity
- * instance with the new tenant-id. Hibernate will _not_ pick up on this and
- * will therefore not save the updated tenant id to database.
- *
- * We have to get hold of the JPA meta model, find the index of the tenantId
- * field and update the entity state in the event.
- */
- @Override
- public boolean onPreInsert(PreInsertEvent event) {
- if (isMultiTenantEntity(event.getEntity())) {
- if (!currentTenant.isSet()) {
- throw new NoCurrentTenantException("Tried to save multi-tenant domain class '"
- + event.getEntity().getClass().getSimpleName() + "', but no tenant is set");
- }
-
- Integer currentTenantId = currentTenant.get();
- hibernateEventPropertyUpdater.updateProperty(event, MultiTenantAST.TENANT_ID_FIELD_NAME, currentTenantId);
- MultiTenantDomainClass entity = (MultiTenantDomainClass) event.getEntity();
- entity.setTenantId(currentTenantId);
- }
-
- return false;
- }
-
- /**
- * TODO: Decide what to do in case tenant X attempts to update an entity
- * without a tenant id. Should we automatically assign this entity to tenant X?
- */
- @Override
- public boolean onPreUpdate(PreUpdateEvent event) {
- if (isMultiTenantEntity(event.getEntity())) {
- Integer currentTenantId = currentTenant.get();
- MultiTenantDomainClass entity = (MultiTenantDomainClass) event.getEntity();
- Integer entityTenantId = entity.getTenantId();
- if (currentTenantId != null && !currentTenantId.equals(entityTenantId)) {
- throw new TenantSecurityException("Tried to update '" + event.getEntity() + "' with another tenant id. Expected "
- + currentTenantId + ", found " + entityTenantId, currentTenantId, entityTenantId);
- }
- }
-
- return false;
- }
-
- @Override
- public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException {
- Object LoadedEntity = event.getResult();
- if (LoadedEntity != null && isMultiTenantEntity(LoadedEntity)) {
- MultiTenantDomainClass entity = (MultiTenantDomainClass) LoadedEntity;
- Integer currentTenantId = currentTenant.get();
-
- // We won't be able to extract tenant-id from an association fetch.
- // TODO: This is a bit scary as it means that we potentially can load entities from
- // other tenants through various variants of Hibernate collection / lazy loading.
- if (!event.isAssociationFetch() && !allowEntityLoad(currentTenantId, entity)) {
- log.debug("Refusing tenant {} to load {}", currentTenant.get(), entity.getClass().getSimpleName());
- event.setResult(null);
- }
- }
- }
-
- protected boolean allowEntityLoad(Integer currentTenantId, MultiTenantDomainClass entity) {
- if (currentTenantId != null) {
- if (belongsToCurrentTenant(currentTenantId, entity)) {
- return true;
- } else {
- log.warn("Tried to fetch an instance of {} belonging to {}", entity.getClass().getName(), entity.getTenantId());
- return false;
- }
- } else {
- return true; // No current tenant => no restrictions
- }
- }
-
- /**
- * This is our last chance to detect attempts to load entities belonging to
- * other tenants.
- */
- @Override
- public void onPostLoad(PostLoadEvent event) {
- // This works, but has the down side that it also triggers an
- // exception for situations we've already handled more gracefully
- // pretending that the entity wasn't found.
-
- // Object entity = event.getEntity();
- // if (isMultiTenantEntity(entity)) {
- // System.out.println("Verifying post load of " + event.getEntity());
- // if (currentTenant.isSet()) {
- // MultiTenantDomainClass tenantEntity = (MultiTenantDomainClass) entity;
- // System.out.println(" -> post load tenant id: " + tenantEntity.getTenantId());
- //
- // Integer tenantEntityId = tenantEntity.getTenantId();
- // if (!currentTenant.get().equals(tenantEntityId)) {
- // System.out.println(" -> Warning! Detected another tenant");
- // throw new TenantSecurityException("Tried to load '" + tenantEntity + "' with another tenant id. Expected "
- // + currentTenant.get() + ", found " + tenantEntityId, currentTenant.get(), tenantEntityId);
- // }
- // }
- // }
- }
-
-
- @Override
- public boolean onPreDelete(PreDeleteEvent event) {
- boolean shouldVetoDelete = false;
- if (isMultiTenantEntity(event.getEntity())) {
- MultiTenantDomainClass tenantEntity = (MultiTenantDomainClass) event.getEntity();
- Integer currentTenantId = currentTenant.get();
-
- if (currentTenantId != null && !belongsToCurrentTenant(currentTenantId, tenantEntity)) {
- log.warn("Tenant {} tried to delete another tenants entity {}", currentTenant.get(), tenantEntity);
- shouldVetoDelete = true;
- }
- }
-
- return shouldVetoDelete;
- }
-
- protected boolean belongsToCurrentTenant(Integer currentTenantId, MultiTenantDomainClass entity) {
- Integer entityTenantId = entity.getTenantId();
- return currentTenantId != null && currentTenantId.equals(entityTenantId);
- }
-
- protected boolean isMultiTenantEntity(Object entity) {
- return entity instanceof MultiTenantDomainClass;
- }
-
- public void setCurrentTenant(CurrentTenant currentTenant) {
- this.currentTenant = currentTenant;
- }
-
- public void setHibernateEventPropertyUpdater(HibernateEventPropertyUpdater updater) {
- this.hibernateEventPropertyUpdater = updater;
- }
-
+package grails.plugin.multitenant.singledb.hibernate;
+
+import grails.plugin.hibernatehijacker.hibernate.events.HibernateEventPropertyUpdater;
+import grails.plugin.multitenant.core.CurrentTenant;
+import grails.plugin.multitenant.core.MultiTenantDomainClass;
+import grails.plugin.multitenant.core.ast.MultiTenantAST;
+import grails.plugin.multitenant.core.exception.NoCurrentTenantException;
+import grails.plugin.multitenant.core.exception.TenantSecurityException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.event.LoadEvent;
+import org.hibernate.event.LoadEventListener;
+import org.hibernate.event.PostLoadEvent;
+import org.hibernate.event.PostLoadEventListener;
+import org.hibernate.event.PreDeleteEvent;
+import org.hibernate.event.PreDeleteEventListener;
+import org.hibernate.event.PreInsertEvent;
+import org.hibernate.event.PreInsertEventListener;
+import org.hibernate.event.PreUpdateEvent;
+import org.hibernate.event.PreUpdateEventListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listens for pre-insert, pre-update and load / fetch Hibernate events. If the
+ * domain class related to the event is a multi-tenant class we apply the
+ * relevant constraints.
+ *
+ * @author Kim A. Betti
+ */
+@SuppressWarnings("serial")
+public class TenantHibernateEventListener implements PreInsertEventListener, PreUpdateEventListener, LoadEventListener, PostLoadEventListener, PreDeleteEventListener {
+
+ private static Logger log = LoggerFactory.getLogger(TenantHibernateFilterConfigurator.class);
+
+ private CurrentTenant currentTenant;
+ private HibernateEventPropertyUpdater hibernateEventPropertyUpdater;
+
+ /**
+ * One important thing to know. It's not enough to update the entity
+ * instance with the new tenant-id. Hibernate will _not_ pick up on this and
+ * will therefore not save the updated tenant id to database.
+ *
+ * We have to get hold of the JPA meta model, find the index of the tenantId
+ * field and update the entity state in the event.
+ */
+ @Override
+ public boolean onPreInsert(PreInsertEvent event) {
+ if (isMultiTenantEntity(event.getEntity())) {
+ if (!currentTenant.isSet()) {
+ throw new NoCurrentTenantException("Tried to save multi-tenant domain class '"
+ + event.getEntity().getClass().getSimpleName() + "', but no tenant is set");
+ }
+
+ Integer currentTenantId = currentTenant.get();
+ hibernateEventPropertyUpdater.updateProperty(event, MultiTenantAST.TENANT_ID_FIELD_NAME, currentTenantId);
+ MultiTenantDomainClass entity = (MultiTenantDomainClass) event.getEntity();
+ entity.setTenantId(currentTenantId);
+ }
+
+ return false;
+ }
+
+ /**
+ * TODO: Decide what to do in case tenant X attempts to update an entity
+ * without a tenant id. Should we automatically assign this entity to tenant X?
+ */
+ @Override
+ public boolean onPreUpdate(PreUpdateEvent event) {
+ if (isMultiTenantEntity(event.getEntity())) {
+ Integer currentTenantId = currentTenant.get();
+ MultiTenantDomainClass entity = (MultiTenantDomainClass) event.getEntity();
+ Integer entityTenantId = entity.getTenantId();
+ if (currentTenantId != null && !currentTenantId.equals(entityTenantId)) {
+ throw new TenantSecurityException("Tried to update '" + event.getEntity() + "' with another tenant id. Expected "
+ + currentTenantId + ", found " + entityTenantId, currentTenantId, entityTenantId);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException {
+ Object LoadedEntity = event.getResult();
+ if (LoadedEntity != null && isMultiTenantEntity(LoadedEntity)) {
+ MultiTenantDomainClass entity = (MultiTenantDomainClass) LoadedEntity;
+ Integer currentTenantId = currentTenant.get();
+
+ // We won't be able to extract tenant-id from an association fetch.
+ // TODO: This is a bit scary as it means that we potentially can load entities from
+ // other tenants through various variants of Hibernate collection / lazy loading.
+ if (!event.isAssociationFetch() && !allowEntityLoad(currentTenantId, entity)) {
+ log.debug("Refusing tenant {} to load {}", currentTenant.get(), entity.getClass().getSimpleName());
+ event.setResult(null);
+ }
+ }
+ }
+
+ protected boolean allowEntityLoad(Integer currentTenantId, MultiTenantDomainClass entity) {
+ if (currentTenantId != null) {
+ if (belongsToCurrentTenant(currentTenantId, entity)) {
+ return true;
+ } else {
+ log.warn("Tried to fetch an instance of {} belonging to {}", entity.getClass().getName(), entity.getTenantId());
+ return false;
+ }
+ } else {
+ return true; // No current tenant => no restrictions
+ }
+ }
+
+ /**
+ * This is our last chance to detect attempts to load entities belonging to
+ * other tenants.
+ */
+ @Override
+ public void onPostLoad(PostLoadEvent event) {
+ // This works, but has the down side that it also triggers an
+ // exception for situations we've already handled more gracefully
+ // pretending that the entity wasn't found.
+
+ // Object entity = event.getEntity();
+ // if (isMultiTenantEntity(entity)) {
+ // System.out.println("Verifying post load of " + event.getEntity());
+ // if (currentTenant.isSet()) {
+ // MultiTenantDomainClass tenantEntity = (MultiTenantDomainClass) entity;
+ // System.out.println(" -> post load tenant id: " + tenantEntity.getTenantId());
+ //
+ // Integer tenantEntityId = tenantEntity.getTenantId();
+ // if (!currentTenant.get().equals(tenantEntityId)) {
+ // System.out.println(" -> Warning! Detected another tenant");
+ // throw new TenantSecurityException("Tried to load '" + tenantEntity + "' with another tenant id. Expected "
+ // + currentTenant.get() + ", found " + tenantEntityId, currentTenant.get(), tenantEntityId);
+ // }
+ // }
+ // }
+ }
+
+
+ @Override
+ public boolean onPreDelete(PreDeleteEvent event) {
+ boolean shouldVetoDelete = false;
+ if (isMultiTenantEntity(event.getEntity())) {
+ MultiTenantDomainClass tenantEntity = (MultiTenantDomainClass) event.getEntity();
+ Integer currentTenantId = currentTenant.get();
+
+ if (currentTenantId != null && !belongsToCurrentTenant(currentTenantId, tenantEntity)) {
+ log.warn("Tenant {} tried to delete another tenants entity {}", currentTenant.get(), tenantEntity);
+ shouldVetoDelete = true;
+ }
+ }
+
+ return shouldVetoDelete;
+ }
+
+ protected boolean belongsToCurrentTenant(Integer currentTenantId, MultiTenantDomainClass entity) {
+ Integer entityTenantId = entity.getTenantId();
+ return currentTenantId != null && currentTenantId.equals(entityTenantId);
+ }
+
+ protected boolean isMultiTenantEntity(Object entity) {
+ return entity instanceof MultiTenantDomainClass;
+ }
+
+ public void setCurrentTenant(CurrentTenant currentTenant) {
+ this.currentTenant = currentTenant;
+ }
+
+ public void setHibernateEventPropertyUpdater(HibernateEventPropertyUpdater updater) {
+ this.hibernateEventPropertyUpdater = updater;
+ }
+
}
View
290 test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerSpec.groovy
@@ -1,145 +1,145 @@
-package grails.plugin.multitenant.singledb.hibernate
-
-import org.hibernate.event.PreDeleteEvent
-import org.hibernate.event.PreInsertEvent;
-import org.hibernate.event.PreUpdateEvent;
-import org.hibernate.persister.entity.EntityPersister;
-
-import spock.lang.Unroll;
-
-import grails.plugin.hibernatehijacker.hibernate.events.HibernateEventPropertyUpdater
-import grails.plugin.multitenant.core.CurrentTenant;
-import grails.plugin.multitenant.core.MultiTenantDomainClass;
-import grails.plugin.multitenant.core.Tenant;
-import grails.plugin.multitenant.core.exception.NoCurrentTenantException;
-import grails.plugin.multitenant.core.exception.TenantException;
-import grails.plugin.multitenant.core.exception.TenantSecurityException;
-import grails.plugin.spock.UnitSpec;
-
-
-/**
- *
- * @author Kim A. Betti
- */
-class TenantHibernateEventListenerSpec extends UnitSpec {
-
- TenantHibernateEventListener eventListener
-
- def setup() {
- eventListener = new TenantHibernateEventListener()
- }
-
- def "pre insert without current tenant throws exception"() {
- given: "a pre insert event"
- DummyEntity entity = new DummyEntity()
- PreInsertEvent event = new PreInsertEvent(entity, null, null, null, null)
-
- and: "a mocked currentTenant bean"
- eventListener.currentTenant = Mock(CurrentTenant)
-
- when: "the event listener is invoked"
- eventListener.onPreInsert(event)
-
- then: "currentTenant is asked and returns null"
- eventListener.currentTenant.isSet() >> false
-
- and: "exception is thrown"
- NoCurrentTenantException ex = thrown()
- }
-
- def "tenant id is sat on the entity and event state on preInsert"() {
- given:
- DummyEntity entity = new DummyEntity()
- PreInsertEvent event = new PreInsertEvent(entity, null, null, null, null)
-
- and: "current tenant returning 123 as tenant id"
- eventListener.currentTenant = Mock(CurrentTenant)
- 1 * eventListener.currentTenant.isSet() >> true
- 1 * eventListener.currentTenant.get() >> 123
-
- and: "mocked event state updater"
- HibernateEventPropertyUpdater propertyUpdater = Mock()
- eventListener.hibernateEventPropertyUpdater = propertyUpdater
-
- when: "we invoke the event method"
- boolean vetoInsert = eventListener.onPreInsert(event)
-
- then: "event state is updated"
- 1 * propertyUpdater.updateProperty(event, "tenantId", 123)
-
- and: "the actual entity instance is updated"
- entity.tenantId == 123
-
- and: "we dont veto the event"
- vetoInsert == false
- }
-
- @Unroll({"Allow #currentTenantId to allow an entity owned by #entityTenantId, #message"})
- def "allowEntityLoad tests"() {
- expect:
- def entity = new DummyEntity(tenantId: entityTenantId)
- eventListener.allowEntityLoad(currentTenantId, entity) == shouldAllow
-
- where:
- currentTenantId | entityTenantId | shouldAllow | message
- null | 123 | true | "No tenant, no restriction"
- 123 | 123 | true | "Must be able to load its own entities"
- 123 | 321 | false | "Should not be allowed to load others"
- 123 | null | false | "We dont allow loading of entities without tenant id" // Can be discussed..
- }
-
- @Unroll({"Tenant id #tenantId, current tenant #currentTenantId, belongs to current tenant #belongsToCurrent"})
- def "belongsToCurrentTenant tests"() {
- expect:
- def entity = new DummyEntity(tenantId: entityTenantId)
- eventListener.belongsToCurrentTenant(currentTenantId, entity) == belongsToCurrent
-
- where:
- currentTenantId | entityTenantId | belongsToCurrent
- 123 | 123 | true
- 123 | null | false
- null | 123 | false
- null | null | false
- }
-
- def "update without tenant id is allowed"() {
- given: "a mocked currentTenant bean"
- eventListener.currentTenant = Mock(CurrentTenant)
- eventListener.currentTenant.get() >> null
-
- expect: "the listener should not veto the event"
- def entity = new DummyEntity(tenantId: 123)
- def preUpdateEvent = new PreUpdateEvent(entity, null, null, null, null, null)
- eventListener.onPreUpdate(preUpdateEvent) == false
- }
-
- def "delete without tenant id is allowed"() {
- given: "a mocked currentTenant bean"
- eventListener.currentTenant = Mock(CurrentTenant)
- eventListener.currentTenant.get() >> null
-
- expect: "the listener should not veto the event"
- def entity = new DummyEntity(tenantId: 123)
- def preUpdateEvent = new PreDeleteEvent(entity, null, null, null, null)
- eventListener.onPreDelete(preUpdateEvent) == false
- }
-
- def "attempts to update another tenants entity should throw an exception"() {
- given: "a mocked currentTenant bean"
- eventListener.currentTenant = Mock(CurrentTenant)
- eventListener.currentTenant.get() >> 123
-
- when: "we try to update anther tenants entity"
- def entity = new DummyEntity(tenantId: 456)
- def preUpdateEvent = new PreUpdateEvent(entity, null, null, null, null, null)
- eventListener.onPreUpdate(preUpdateEvent)
-
- then: "a tenant security exception is thrown"
- thrown(TenantSecurityException)
- }
-
-}
-
-class DummyEntity implements MultiTenantDomainClass {
- Integer tenantId
-}
+package grails.plugin.multitenant.singledb.hibernate
+
+import org.hibernate.event.PreDeleteEvent
+import org.hibernate.event.PreInsertEvent;
+import org.hibernate.event.PreUpdateEvent;
+import org.hibernate.persister.entity.EntityPersister;
+
+import spock.lang.Unroll;
+
+import grails.plugin.hibernatehijacker.hibernate.events.HibernateEventPropertyUpdater
+import grails.plugin.multitenant.core.CurrentTenant;
+import grails.plugin.multitenant.core.MultiTenantDomainClass;
+import grails.plugin.multitenant.core.Tenant;
+import grails.plugin.multitenant.core.exception.NoCurrentTenantException;
+import grails.plugin.multitenant.core.exception.TenantException;
+import grails.plugin.multitenant.core.exception.TenantSecurityException;
+import grails.plugin.spock.UnitSpec;
+
+
+/**
+ *
+ * @author Kim A. Betti
+ */
+class TenantHibernateEventListenerSpec extends UnitSpec {
+
+ TenantHibernateEventListener eventListener
+
+ def setup() {
+ eventListener = new TenantHibernateEventListener()
+ }
+
+ def "pre insert without current tenant throws exception"() {
+ given: "a pre insert event"
+ DummyEntity entity = new DummyEntity()
+ PreInsertEvent event = new PreInsertEvent(entity, null, null, null, null)
+
+ and: "a mocked currentTenant bean"
+ eventListener.currentTenant = Mock(CurrentTenant)
+
+ when: "the event listener is invoked"
+ eventListener.onPreInsert(event)
+
+ then: "currentTenant is asked and returns null"
+ eventListener.currentTenant.isSet() >> false
+
+ and: "exception is thrown"
+ NoCurrentTenantException ex = thrown()
+ }
+
+ def "tenant id is sat on the entity and event state on preInsert"() {
+ given:
+ DummyEntity entity = new DummyEntity()
+ PreInsertEvent event = new PreInsertEvent(entity, null, null, null, null)
+
+ and: "current tenant returning 123 as tenant id"
+ eventListener.currentTenant = Mock(CurrentTenant)
+ 1 * eventListener.currentTenant.isSet() >> true
+ 1 * eventListener.currentTenant.get() >> 123
+
+ and: "mocked event state updater"
+ HibernateEventPropertyUpdater propertyUpdater = Mock()
+ eventListener.hibernateEventPropertyUpdater = propertyUpdater
+
+ when: "we invoke the event method"
+ boolean vetoInsert = eventListener.onPreInsert(event)
+
+ then: "event state is updated"
+ 1 * propertyUpdater.updateProperty(event, "tenantId", 123)
+
+ and: "the actual entity instance is updated"
+ entity.tenantId == 123
+
+ and: "we dont veto the event"
+ vetoInsert == false
+ }
+
+ @Unroll({"Allow #currentTenantId to allow an entity owned by #entityTenantId, #message"})
+ def "allowEntityLoad tests"() {
+ expect:
+ def entity = new DummyEntity(tenantId: entityTenantId)
+ eventListener.allowEntityLoad(currentTenantId, entity) == shouldAllow
+
+ where:
+ currentTenantId | entityTenantId | shouldAllow | message
+ null | 123 | true | "No tenant, no restriction"
+ 123 | 123 | true | "Must be able to load its own entities"
+ 123 | 321 | false | "Should not be allowed to load others"
+ 123 | null | false | "We dont allow loading of entities without tenant id" // Can be discussed..
+ }
+
+ @Unroll({"Tenant id #tenantId, current tenant #currentTenantId, belongs to current tenant #belongsToCurrent"})
+ def "belongsToCurrentTenant tests"() {
+ expect:
+ def entity = new DummyEntity(tenantId: entityTenantId)
+ eventListener.belongsToCurrentTenant(currentTenantId, entity) == belongsToCurrent
+
+ where:
+ currentTenantId | entityTenantId | belongsToCurrent
+ 123 | 123 | true
+ 123 | null | false
+ null | 123 | false
+ null | null | false
+ }
+
+ def "update without tenant id is allowed"() {
+ given: "a mocked currentTenant bean"
+ eventListener.currentTenant = Mock(CurrentTenant)
+ eventListener.currentTenant.get() >> null
+
+ expect: "the listener should not veto the event"
+ def entity = new DummyEntity(tenantId: 123)
+ def preUpdateEvent = new PreUpdateEvent(entity, null, null, null, null, null)
+ eventListener.onPreUpdate(preUpdateEvent) == false
+ }
+
+ def "delete without tenant id is allowed"() {
+ given: "a mocked currentTenant bean"
+ eventListener.currentTenant = Mock(CurrentTenant)
+ eventListener.currentTenant.get() >> null
+
+ expect: "the listener should not veto the event"
+ def entity = new DummyEntity(tenantId: 123)
+ def preUpdateEvent = new PreDeleteEvent(entity, null, null, null, null)
+ eventListener.onPreDelete(preUpdateEvent) == false
+ }
+
+ def "attempts to update another tenants entity should throw an exception"() {
+ given: "a mocked currentTenant bean"
+ eventListener.currentTenant = Mock(CurrentTenant)
+ eventListener.currentTenant.get() >> 123
+
+ when: "we try to update anther tenants entity"
+ def entity = new DummyEntity(tenantId: 456)
+ def preUpdateEvent = new PreUpdateEvent(entity, null, null, null, null, null)
+ eventListener.onPreUpdate(preUpdateEvent)
+
+ then: "a tenant security exception is thrown"
+ thrown(TenantSecurityException)
+ }
+
+}
+
+class DummyEntity implements MultiTenantDomainClass {
+ Integer tenantId
+}
Please sign in to comment.
Something went wrong with that request. Please try again.