├── LICENSE
├── README.md
└── bookstore
├── .gitignore
├── conf
├── bookstore.properties
└── logback.xml
├── pom.xml
└── src
├── main
├── java
│ └── sample
│ │ └── sdr
│ │ └── auth
│ │ ├── audit
│ │ └── AuditingRevisionListener.java
│ │ ├── bean
│ │ ├── AbstractSecuredEntity.java
│ │ ├── AuditRevision.java
│ │ ├── Book.java
│ │ ├── Person.java
│ │ ├── RoleEntity.java
│ │ └── UserEntity.java
│ │ ├── config
│ │ └── CustomRepositoryRestMvcConfiguration.java
│ │ ├── controller
│ │ ├── ACLController.java
│ │ └── LoginController.java
│ │ ├── dao
│ │ └── SecurityACLDAO.java
│ │ ├── dto
│ │ ├── UserACLRequest.java
│ │ └── UserACLRequestSet.java
│ │ ├── log
│ │ └── BookstoreLogger.java
│ │ ├── repo
│ │ └── handlers
│ │ │ └── BookHandler.java
│ │ ├── repositories
│ │ ├── BookRepository.java
│ │ ├── RoleEntityRepo.java
│ │ └── UserEntityRepo.java
│ │ ├── security
│ │ ├── CustomUserDetailsService.java
│ │ └── SecurityUtil.java
│ │ └── util
│ │ └── Constants.java
├── resources
│ ├── META-INF
│ │ └── spring-data-rest
│ │ │ └── repositories-export.xml
│ ├── ehcache-hibernate.xml
│ └── logback.xml
└── webapp
│ └── WEB-INF
│ ├── bookstore-auth-config.xml
│ ├── bookstore-auth-rules.xml
│ ├── bookstore-context.xml
│ ├── bookstore-dao.xml
│ ├── bookstore-dbconfig.xml
│ ├── bookstore-jpaconfig.xml
│ ├── bookstore-sdr-auth.xml
│ ├── bookstore-security.xml
│ ├── bookstore-servlet.xml
│ ├── bookstore-spring-data-rest.xml
│ ├── bookstore-test-context.xml
│ ├── bookstore-test-servlet.xml
│ └── web.xml
├── scripts
└── sql
│ ├── bookstoreConfig.sql
│ └── createAclSchemaPostgres.sql
└── test
├── curl
└── bookstorecurl.txt
├── java
└── sample
│ └── sdr
│ └── auth
│ └── test
│ └── api
│ └── TestBookCRUD.java
└── resources
├── bookstore-test.properties
├── ehcache-hibernate-test.xml
└── logback-test.xml
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | spring-data-rest-acl
2 | ====================
3 |
4 | ## Spring Security ACL with Spring Data REST
5 |
6 | This is a sample project to illustrate how to use ACL-based Authorization for entities exposed by Spring Data REST.
7 | This is referred by:
8 | http://stackoverflow.com/questions/26546072/using-spring-security-acl-with-spring-data-rest
9 |
10 | #### Steps to deploy and run Bookstore sample:
11 |
12 | Note that "conf" directory contains config files that needs to be placed at /etc/bookstore/.
13 | - bookstore.properties for configuring database, hibernate, connection-pooling
14 | - logback.xml for setting log levels. Set log file location - /var/log/bookstore/bookstore.log
15 |
16 | 1. create db user book with password book:
17 | createuser --createdb --no-createrole -P book
18 |
19 | 2. create db:
20 | createdb bookstore -O book -E 'UTF8'
21 |
22 | 3. Run Spring Security ACL schema - bookstore/src/scripts/sql/createAclSchemaPostgres.sql
23 |
24 | 4. Do a maven build and copy bookstore.war under tomcat/webapps.
25 |
26 | 5. Run tomcat
27 |
28 | 6. Run bookstore config sql - bookstore/src/scripts/sql/bookstoreConfig.sql
29 |
30 | 7. Go over the curl samples in bookstore/src/test/curl/bookstorecurl.txt to see how it works.
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/bookstore/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /target/
3 |
--------------------------------------------------------------------------------
/bookstore/conf/bookstore.properties:
--------------------------------------------------------------------------------
1 | logback.configloc=/etc/bookstore/logback.xml
2 |
3 | jdbc.dataSourceClassName=com.impossibl.postgres.jdbc.PGDataSource
4 | jdbc.dbserver=localhost
5 | jdbc.dbport=5432
6 | jdbc.dbname=bookstore
7 | jdbc.username=book
8 | jdbc.password=book
9 |
10 | hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
11 | hibernate.show_sql=false
12 | hibernate.cacheconfig=/ehcache-hibernate.xml
13 |
14 | #hikaricp properties - connection pooling
15 | hikari.maxPoolSize=10
16 | pgjdbc-ng.housekeeper=false
17 |
--------------------------------------------------------------------------------
/bookstore/conf/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
8 | /var/log/bookstore/bookstore.log
9 |
10 |
11 | /var/log/bookstore/bookstore-%d{yyyy-MM-dd}.%i.gz
12 |
13 |
15 |
16 | 5MB
17 |
18 |
19 |
20 | %d [%t] %-5p %logger{0} - %m%n
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/bookstore/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | sample.sdr.auth
5 | bookstore
6 | war
7 | 1.0-SNAPSHOT
8 | Bookstore Maven Webapp
9 | http://maven.apache.org
10 |
11 |
12 | UTF-8
13 | 1.7
14 | 1.7
15 | 3.0.1
16 | 4.0.5.RELEASE
17 | 3.2.5.RELEASE
18 | 1.8.2
19 | 1.6.1.RELEASE
20 | 2.1.0.RELEASE
21 | 0.2.0.RELEASE
22 | 4.11
23 | 0.4
24 | 1.7.7
25 | 2.8.11.1
26 | 4.3.5.Final
27 | 1.1.2
28 | 1.3.8
29 | 1.4.7
30 | 2.3.2
31 | 3.1
32 | 2.6
33 | 2.4
34 |
35 |
36 |
37 |
38 | junit
39 | junit
40 | ${junit.version}
41 | test
42 |
43 |
44 | org.springframework
45 | spring-test
46 | ${org.springframework.version}
47 | test
48 |
49 |
50 | org.hsqldb
51 | hsqldb
52 | ${hsqldb.version}
53 | test
54 |
55 |
56 | org.springframework
57 | spring-context
58 | ${org.springframework.version}
59 |
60 |
61 | commons-logging
62 | commons-logging
63 |
64 |
65 |
66 |
67 | org.springframework
68 | spring-context-support
69 | ${org.springframework.version}
70 |
71 |
72 | org.springframework
73 | spring-web
74 | ${org.springframework.version}
75 |
76 |
77 | org.springframework
78 | spring-webmvc
79 | ${org.springframework.version}
80 |
81 |
82 | com.thoughtworks.xstream
83 | xstream
84 | ${com.thoughtworks.xstream.version}
85 |
86 |
87 | org.springframework
88 | spring-orm
89 | ${org.springframework.version}
90 |
91 |
92 | org.springframework
93 | spring-oxm
94 | ${org.springframework.version}
95 |
96 |
97 | org.springframework.security
98 | spring-security-web
99 | ${org.springframework.security.version}
100 |
101 |
102 | org.springframework.security
103 | spring-security-config
104 | ${org.springframework.security.version}
105 |
106 |
107 | org.springframework.security
108 | spring-security-acl
109 | ${org.springframework.security.version}
110 |
111 |
112 | org.aspectj
113 | aspectjweaver
114 | ${org.aspectj.version}
115 |
116 |
117 | org.springframework.data
118 | spring-data-jpa
119 | ${spring-data-jpa.version}
120 |
121 |
122 | org.springframework.data
123 | spring-data-rest-webmvc
124 | ${spring-data-rest.version}
125 |
126 |
127 | org.springframework.data
128 | spring-data-envers
129 | ${spring-data-envers.version}
130 |
131 |
132 | com.impossibl.pgjdbc-ng
133 | pgjdbc-ng
134 | ${pgjdbc-ng.version}
135 |
136 |
137 | javax.servlet
138 | javax.servlet-api
139 | ${javax.servlet.version}
140 | provided
141 |
142 |
143 | org.hibernate
144 | hibernate-entitymanager
145 | ${org.hibernate.version}
146 |
147 |
148 | org.hibernate
149 | hibernate-envers
150 | ${org.hibernate.version}
151 |
152 |
153 |
154 | org.hibernate
155 | hibernate-ehcache
156 | ${org.hibernate.version}
157 |
158 |
159 | com.fasterxml.jackson.core
160 | jackson-databind
161 | ${com.fasterxml.jackson.core.version}
162 |
163 |
164 | ch.qos.logback
165 | logback-classic
166 | ${ch.qos.logback.version}
167 |
168 |
169 | org.slf4j
170 | jcl-over-slf4j
171 | ${org.slf4j.version}
172 |
173 |
174 | org.slf4j
175 | log4j-over-slf4j
176 | ${org.slf4j.version}
177 |
178 |
179 | com.zaxxer
180 | HikariCP
181 | ${hikaricp.version}
182 | compile
183 |
184 |
185 |
186 |
187 | bookstore
188 |
189 |
190 | org.apache.maven.plugins
191 | maven-compiler-plugin
192 | ${maven-compiler-plugin.version}
193 |
194 | ${java.source.version}
195 | ${java.target.version}
196 |
197 |
198 |
199 | org.apache.maven.plugins
200 | maven-war-plugin
201 | ${maven-war-plugin.version}
202 |
203 | true
204 |
205 |
206 |
207 |
208 |
209 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/audit/AuditingRevisionListener.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.audit;
2 |
3 | import org.hibernate.envers.RevisionListener;
4 | import org.springframework.security.core.context.SecurityContextHolder;
5 | import org.springframework.security.web.authentication.WebAuthenticationDetails;
6 |
7 | import sample.sdr.auth.bean.AuditRevision;
8 | import sample.sdr.auth.security.SecurityUtil;
9 |
10 |
11 | public class AuditingRevisionListener implements RevisionListener {
12 |
13 | @Override
14 | public void newRevision(Object revisionEntity) {
15 | AuditRevision auditedRevision = (AuditRevision) revisionEntity;
16 | String userName = SecurityUtil.getUsername();
17 | /* possible approach to get IP address of the user
18 | - http://stackoverflow.com/questions/12786123/ip-filter-using-spring-security
19 | - http://forum.springsource.org/showthread.php?18071-pass-ip-address-to-authentication-provider
20 | */
21 |
22 | WebAuthenticationDetails auth = (WebAuthenticationDetails)
23 | SecurityContextHolder.getContext().getAuthentication().getDetails();
24 | if(auth != null) {
25 | String ipAddress = auth.getRemoteAddress();
26 | auditedRevision.setIpAddress(ipAddress);
27 | }
28 |
29 | auditedRevision.setUsername(userName);
30 | }
31 | }
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/bean/AbstractSecuredEntity.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.bean;
2 |
3 | import java.io.Serializable;
4 |
5 | import javax.persistence.GeneratedValue;
6 | import javax.persistence.Id;
7 | import javax.persistence.MappedSuperclass;
8 | import javax.xml.bind.annotation.XmlRootElement;
9 |
10 | import org.hibernate.envers.Audited;
11 |
12 | @Audited
13 | @XmlRootElement(name="AbstractSecuredEntity")
14 | @MappedSuperclass
15 | public abstract class AbstractSecuredEntity implements Serializable {
16 |
17 | private static final long serialVersionUID = 1L;
18 |
19 | @Id
20 | @GeneratedValue
21 | private long id;
22 |
23 | public long getId() {
24 | return id;
25 | }
26 |
27 | public void setId(long id) {
28 | this.id = id;
29 | }
30 |
31 | @Override
32 | public int hashCode() {
33 | final int prime = 31;
34 | int result = 1;
35 | result = prime * result + (int) (id ^ (id >>> 32));
36 | return result;
37 | }
38 |
39 | @Override
40 | public boolean equals(Object obj) {
41 | if (this == obj)
42 | return true;
43 | if (obj == null)
44 | return false;
45 | if (getClass() != obj.getClass())
46 | return false;
47 | AbstractSecuredEntity other = (AbstractSecuredEntity) obj;
48 | if (id != other.id)
49 | return false;
50 | return true;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/bean/AuditRevision.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.bean;
2 |
3 | import java.text.DateFormat;
4 |
5 | import javax.persistence.Entity;
6 | import javax.persistence.Table;
7 | import javax.xml.bind.annotation.XmlRootElement;
8 |
9 | import org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity;
10 | import org.hibernate.envers.RevisionEntity;
11 |
12 | import sample.sdr.auth.audit.AuditingRevisionListener;
13 |
14 | @XmlRootElement(name="AuditRevision")
15 | @Entity
16 | @Table(name="AuditRevision")
17 | @RevisionEntity(AuditingRevisionListener.class)
18 | public class AuditRevision extends DefaultTrackingModifiedEntitiesRevisionEntity {
19 |
20 | private static final long serialVersionUID = 1L;
21 |
22 | private String username;
23 |
24 | private String ipAddress;
25 |
26 | public String toString() {
27 | return "AuditRevision(user = " + username + ", id = " + getId()
28 | + ", ipaddress = " + ipAddress
29 | + ", revisionDate = " + DateFormat.getDateTimeInstance().format(getRevisionDate()) + ")";
30 | }
31 |
32 | public String getUsername() {
33 | return username;
34 | }
35 |
36 | public void setUsername(String username) {
37 | this.username = username;
38 | }
39 |
40 | public String getIpAddress() {
41 | return ipAddress;
42 | }
43 |
44 | public void setIpAddress(String ipAddress) {
45 | this.ipAddress = ipAddress;
46 | }
47 |
48 | @Override
49 | public int hashCode() {
50 | final int prime = 31;
51 | int result = super.hashCode();
52 | result = prime * result
53 | + ((ipAddress == null) ? 0 : ipAddress.hashCode());
54 | result = prime * result
55 | + ((username == null) ? 0 : username.hashCode());
56 | return result;
57 | }
58 |
59 | @Override
60 | public boolean equals(Object obj) {
61 | if (this == obj)
62 | return true;
63 | if (!super.equals(obj))
64 | return false;
65 | if (getClass() != obj.getClass())
66 | return false;
67 | AuditRevision other = (AuditRevision) obj;
68 | if (ipAddress == null) {
69 | if (other.ipAddress != null)
70 | return false;
71 | } else if (!ipAddress.equals(other.ipAddress))
72 | return false;
73 | if (username == null) {
74 | if (other.username != null)
75 | return false;
76 | } else if (!username.equals(other.username))
77 | return false;
78 | return true;
79 | }
80 | }
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/bean/Book.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.bean;
2 |
3 | import javax.persistence.Entity;
4 | import javax.xml.bind.annotation.XmlRootElement;
5 |
6 | import org.hibernate.envers.Audited;
7 |
8 | @Audited
9 | @XmlRootElement(name = "Book")
10 | @Entity
11 | public class Book extends AbstractSecuredEntity {
12 | private static final long serialVersionUID = 1L;
13 |
14 | private String bookName;
15 |
16 | private String description;
17 |
18 | private String author;
19 |
20 | public String getBookName() {
21 | return bookName;
22 | }
23 |
24 | public void setBookName(String bookName) {
25 | this.bookName = bookName;
26 | }
27 |
28 | public String getDescription() {
29 | return description;
30 | }
31 |
32 | public void setDescription(String description) {
33 | this.description = description;
34 | }
35 |
36 | public String getAuthor() {
37 | return author;
38 | }
39 |
40 | public void setAuthor(String author) {
41 | this.author = author;
42 | }
43 |
44 | @Override
45 | public String toString() {
46 | return "Book [bookName=" + bookName + ", description=" + description
47 | + ", author=" + author + "]";
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/bean/Person.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.bean;
2 |
3 | import javax.persistence.MappedSuperclass;
4 | import javax.xml.bind.annotation.XmlRootElement;
5 |
6 | import org.hibernate.envers.Audited;
7 |
8 | @Audited
9 | @XmlRootElement(name="Person")
10 | @MappedSuperclass
11 | public abstract class Person extends AbstractSecuredEntity {
12 | private static final long serialVersionUID = 1L;
13 |
14 | private String salutation;
15 | private String firstName;
16 | private String lastName;
17 | private String email;
18 | private String mobile;
19 |
20 | @Override
21 | public int hashCode() {
22 | final int prime = 31;
23 | int result = super.hashCode();
24 | result = prime * result + ((email == null) ? 0 : email.hashCode());
25 | return result;
26 | }
27 | @Override
28 | public boolean equals(Object obj) {
29 | if (this == obj)
30 | return true;
31 | if (!super.equals(obj))
32 | return false;
33 | if (getClass() != obj.getClass())
34 | return false;
35 | Person other = (Person) obj;
36 | if (email == null) {
37 | if (other.email != null)
38 | return false;
39 | } else if (!email.equals(other.email))
40 | return false;
41 | return true;
42 | }
43 | public String getSalutation() {
44 | return salutation;
45 | }
46 | public void setSalutation(String salutation) {
47 | this.salutation = salutation;
48 | }
49 | public String getFirstName() {
50 | return firstName;
51 | }
52 | public void setFirstName(String firstName) {
53 | this.firstName = firstName;
54 | }
55 | public String getLastName() {
56 | return lastName;
57 | }
58 | public void setLastName(String lastName) {
59 | this.lastName = lastName;
60 | }
61 | public String getEmail() {
62 | return email;
63 | }
64 | public void setEmail(String email) {
65 | this.email = email;
66 | }
67 | public String getMobile() {
68 | return mobile;
69 | }
70 | public void setMobile(String mobile) {
71 | this.mobile = mobile;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/bean/RoleEntity.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.bean;
2 |
3 | import java.util.HashSet;
4 | import java.util.Locale;
5 | import java.util.Set;
6 |
7 | import javax.persistence.Column;
8 | import javax.persistence.Entity;
9 | import javax.persistence.ManyToMany;
10 | import javax.xml.bind.annotation.XmlRootElement;
11 |
12 | @Entity
13 | @XmlRootElement(name = "RoleEntity")
14 | public class RoleEntity extends AbstractSecuredEntity {
15 | private static final long serialVersionUID = 1L;
16 |
17 | /**
18 | * converts to upper case, to make it case-insensitive
19 | */
20 | @Column(unique = true, nullable = false)
21 | private String authority;
22 |
23 | @ManyToMany(mappedBy = "roles")
24 | private Set users = new HashSet();
25 |
26 | public String getAuthority() {
27 | return authority;
28 | }
29 |
30 | public void setAuthority(String authority) {
31 | this.authority = authority.toUpperCase(Locale.ENGLISH);
32 | }
33 |
34 | public Set getUsers() {
35 | return users;
36 | }
37 |
38 | public void setUsers(Set users) {
39 | this.users = users;
40 | }
41 |
42 | @Override
43 | public int hashCode() {
44 | final int prime = 31;
45 | int result = super.hashCode();
46 | result = prime * result
47 | + ((authority == null) ? 0 : authority.hashCode());
48 | return result;
49 | }
50 |
51 | @Override
52 | public boolean equals(Object obj) {
53 | if (this == obj)
54 | return true;
55 | if (!super.equals(obj))
56 | return false;
57 | if (getClass() != obj.getClass())
58 | return false;
59 | RoleEntity other = (RoleEntity) obj;
60 | if (authority == null) {
61 | if (other.authority != null)
62 | return false;
63 | } else if (!authority.equals(other.authority))
64 | return false;
65 | return true;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/bean/UserEntity.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.bean;
2 |
3 | import java.util.HashSet;
4 | import java.util.Locale;
5 | import java.util.Set;
6 |
7 | import javax.persistence.Column;
8 | import javax.persistence.Entity;
9 | import javax.persistence.FetchType;
10 | import javax.persistence.ManyToMany;
11 | import javax.xml.bind.annotation.XmlRootElement;
12 |
13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14 | import org.springframework.security.crypto.password.PasswordEncoder;
15 |
16 | @Entity
17 | @XmlRootElement(name="UserEntity")
18 | public class UserEntity extends Person {
19 | private static final long serialVersionUID = 1L;
20 |
21 | /**
22 | * converts to lower case, to make it case-insensitive
23 | */
24 | @Column(unique=true, nullable=false)
25 | private String username;
26 |
27 | /**
28 | * encoded password
29 | */
30 | @Column(nullable=false)
31 | private String password;
32 |
33 | private boolean enabled;
34 |
35 | @ManyToMany(fetch=FetchType.EAGER)
36 | private Set roles = new HashSet();
37 |
38 | public void setPassword(String password) {
39 | PasswordEncoder encoder = new BCryptPasswordEncoder();
40 | this.password = encoder.encode(password);
41 | }
42 |
43 | public String getUsername() {
44 | return username;
45 | }
46 |
47 | public void setUsername(String username) {
48 | this.username = username.toLowerCase(Locale.ENGLISH);
49 | }
50 |
51 | public String getPassword() {
52 | return password;
53 | }
54 |
55 | public boolean isEnabled() {
56 | return enabled;
57 | }
58 |
59 | public void setEnabled(boolean enabled) {
60 | this.enabled = enabled;
61 | }
62 |
63 | public Set getRoles() {
64 | return roles;
65 | }
66 |
67 | public void setRoles(Set roles) {
68 | this.roles = roles;
69 | }
70 |
71 | @Override
72 | public int hashCode() {
73 | final int prime = 31;
74 | int result = super.hashCode();
75 | result = prime * result
76 | + ((username == null) ? 0 : username.hashCode());
77 | return result;
78 | }
79 |
80 | @Override
81 | public boolean equals(Object obj) {
82 | if (this == obj)
83 | return true;
84 | if (!super.equals(obj))
85 | return false;
86 | if (getClass() != obj.getClass())
87 | return false;
88 | UserEntity other = (UserEntity) obj;
89 | if (username == null) {
90 | if (other.username != null)
91 | return false;
92 | } else if (!username.equals(other.username))
93 | return false;
94 | return true;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/config/CustomRepositoryRestMvcConfiguration.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
5 | import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
6 |
7 | /**
8 | * To change any default config, we need to subclass
9 | * RepositoryRestMvcConfiguration
10 | *
11 | */
12 | @Configuration
13 | public class CustomRepositoryRestMvcConfiguration extends
14 | RepositoryRestMvcConfiguration {
15 |
16 | @Override
17 | protected void configureRepositoryRestConfiguration(
18 | RepositoryRestConfiguration config) {
19 | config.setDefaultPageSize(1000);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/controller/ACLController.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.controller;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.BeansException;
10 | import org.springframework.beans.factory.BeanFactory;
11 | import org.springframework.beans.factory.BeanFactoryAware;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.data.repository.CrudRepository;
14 | import org.springframework.http.HttpEntity;
15 | import org.springframework.http.HttpStatus;
16 | import org.springframework.http.ResponseEntity;
17 | import org.springframework.security.acls.domain.BasePermission;
18 | import org.springframework.security.acls.domain.PrincipalSid;
19 | import org.springframework.security.acls.model.Permission;
20 | import org.springframework.stereotype.Controller;
21 | import org.springframework.web.bind.annotation.RequestBody;
22 | import org.springframework.web.bind.annotation.RequestMapping;
23 | import org.springframework.web.bind.annotation.RequestMethod;
24 | import org.springframework.web.bind.annotation.ResponseBody;
25 |
26 | import sample.sdr.auth.bean.AbstractSecuredEntity;
27 | import sample.sdr.auth.bean.Book;
28 | import sample.sdr.auth.bean.UserEntity;
29 | import sample.sdr.auth.dao.SecurityACLDAO;
30 | import sample.sdr.auth.dto.UserACLRequest;
31 | import sample.sdr.auth.dto.UserACLRequestSet;
32 | import sample.sdr.auth.repositories.UserEntityRepo;
33 | import sample.sdr.auth.util.Constants;
34 |
35 | @Controller
36 | public class ACLController implements BeanFactoryAware {
37 | private static Logger logger = LoggerFactory.getLogger(ACLController.class);
38 |
39 | private static HashMap classToRepoMap = new HashMap();
40 |
41 | static {
42 | classToRepoMap.put(Book.class.getSimpleName(), Constants.BOOK_REPOSITORY);
43 | }
44 |
45 | private BeanFactory beanFactory;
46 |
47 | @Autowired
48 | private UserEntityRepo userEntityRepo;
49 |
50 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
51 | this.beanFactory = beanFactory;
52 | }
53 |
54 | public static Permission getPermissionFromNumber(int permNum) {
55 | switch(permNum) {
56 | case 0: return BasePermission.READ; //1
57 | case 1: return BasePermission.WRITE; //2
58 | case 2: return BasePermission.CREATE; //4
59 | case 3: return BasePermission.DELETE; //8
60 | case 4: return BasePermission.ADMINISTRATION; //16
61 | default: return null;
62 | }
63 | }
64 |
65 | private CrudRepository getRepo(String repoName) {
66 | return (CrudRepository) beanFactory.getBean(repoName);
67 | }
68 |
69 | @RequestMapping(method = RequestMethod.POST, value = "/acl/user/generic/")
70 | public @ResponseBody HttpEntity> addACLUserGeneric(
71 | @RequestBody UserACLRequestSet aclSet) {
72 | List result = new ArrayList();
73 | try {
74 | SecurityACLDAO securityACLDAO = beanFactory.getBean(
75 | Constants.SECURITYACL_DAO, SecurityACLDAO.class);
76 |
77 | logger.debug("entityList size:" + aclSet.getAclList().size());
78 | String entityId = null;
79 | for (UserACLRequest acl : aclSet.getAclList()) {
80 | entityId = acl.getEntityId();
81 | String repoName = classToRepoMap.get(acl.getEntityClassName());
82 | CrudRepository repo = getRepo(repoName);
83 | AbstractSecuredEntity securedEntity = (AbstractSecuredEntity) repo
84 | .findOne(Long.parseLong(entityId));
85 | if (securedEntity == null) {
86 | result.add("Entity of type " + acl.getEntityClassName()
87 | + " with id " + acl.getEntityId() + " not found");
88 | continue;
89 | }
90 | UserEntity userEntity = userEntityRepo.findByUsername(acl
91 | .getUserName());
92 | if (userEntity == null) {
93 | result.add("User " + acl.getUserName() + " not found");
94 | continue;
95 | }
96 | boolean res = securityACLDAO.addAccessControlEntry(
97 | securedEntity,
98 | new PrincipalSid(userEntity.getUsername()),
99 | getPermissionFromNumber(acl.getPermission()));
100 | if (res) {
101 | // on sucess don't add msg to result list
102 | logger.debug("Added ACL for:" + acl.toString());
103 | } else {
104 | result.add("Failed to add ACL for:" + acl.toString());
105 | }
106 | }
107 | } catch (Exception e) {
108 | logger.warn("", e);
109 | result.add("Exception:" + e.getMessage());
110 | }
111 | if (result.isEmpty()) {
112 | result.add("Successfully added all ACLs");
113 | return new ResponseEntity>(result, HttpStatus.OK);
114 | } else {
115 | return new ResponseEntity>(result, HttpStatus.OK);
116 | }
117 | }
118 |
119 | @RequestMapping(method = RequestMethod.DELETE, value = "/acl/user/generic/")
120 | public @ResponseBody HttpEntity> removeACLUserGeneric(
121 | @RequestBody UserACLRequestSet aclSet) {
122 | List result = new ArrayList();
123 | try {
124 | SecurityACLDAO securityACLDAO = beanFactory.getBean(
125 | Constants.SECURITYACL_DAO, SecurityACLDAO.class);
126 |
127 | logger.debug("entityList size:" + aclSet.getAclList().size());
128 | String entityId = null;
129 | for (UserACLRequest acl : aclSet.getAclList()) {
130 | entityId = acl.getEntityId();
131 | String repoName = classToRepoMap.get(acl.getEntityClassName());
132 | CrudRepository repo = getRepo(repoName);
133 | AbstractSecuredEntity securedEntity = (AbstractSecuredEntity) repo
134 | .findOne(Long.parseLong(entityId));
135 | if (securedEntity == null) {
136 | result.add("Entity of type " + acl.getEntityClassName()
137 | + " with id " + acl.getEntityId() + " not found");
138 | continue;
139 | }
140 | UserEntity userEntity = userEntityRepo.findByUsername(acl
141 | .getUserName());
142 | if (userEntity == null) {
143 | result.add("User " + acl.getUserName() + " not found");
144 | continue;
145 | }
146 | boolean res = securityACLDAO.deleteAccessControlEntry(
147 | securedEntity,
148 | new PrincipalSid(userEntity.getUsername()),
149 | getPermissionFromNumber(acl.getPermission()));
150 | if (res) {
151 | // on sucess don't add msg to result list
152 | logger.debug("Deleted ACL for:" + acl.toString());
153 | } else {
154 | result.add("Failed to add ACL for:" + acl.toString());
155 | }
156 | }
157 | } catch (Exception e) {
158 | logger.warn("", e);
159 | result.add("Exception:" + e.getMessage());
160 | }
161 | if (result.isEmpty()) {
162 | result.add("Successfully deleted all ACLs");
163 | return new ResponseEntity>(result, HttpStatus.OK);
164 | } else {
165 | return new ResponseEntity>(result, HttpStatus.OK);
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/controller/LoginController.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.controller;
2 |
3 | import java.security.Principal;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import javax.servlet.http.HttpSession;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.http.HttpStatus;
12 | import org.springframework.http.ResponseEntity;
13 | import org.springframework.stereotype.Controller;
14 | import org.springframework.web.bind.annotation.RequestMapping;
15 | import org.springframework.web.bind.annotation.RequestMethod;
16 | import org.springframework.web.bind.annotation.RequestParam;
17 | import org.springframework.web.bind.annotation.ResponseBody;
18 |
19 | import sample.sdr.auth.security.SecurityUtil;
20 |
21 | @Controller
22 | public class LoginController {
23 | protected static Logger logger = LoggerFactory
24 | .getLogger(LoginController.class);
25 |
26 | @RequestMapping(method = RequestMethod.GET, value = "/login_success")
27 | public @ResponseBody
28 | String handleLoginSuccess(Principal principal) {
29 | return SecurityUtil.getUserRoles().toString();
30 | }
31 |
32 | @RequestMapping(method = RequestMethod.GET, value = "/all/login_failure")
33 | public @ResponseBody
34 | ResponseEntity handleLoginFailure(Principal principal) {
35 | return new ResponseEntity<>("Login failed", HttpStatus.UNAUTHORIZED);
36 | }
37 |
38 | @RequestMapping(method = RequestMethod.GET, value = "/all/logout")
39 | public @ResponseBody
40 | String handleLogout() {
41 | return "Logout successful";
42 | }
43 |
44 | @RequestMapping(method = RequestMethod.GET, value = "/all/invalid_session")
45 | public @ResponseBody
46 | ResponseEntity handleInvalidSession(HttpServletRequest request,
47 | HttpServletResponse response) {
48 | return new ResponseEntity<>("Logged in from a different device",
49 | HttpStatus.REQUEST_TIMEOUT);
50 | }
51 |
52 | /**
53 | * api to set session timeout for current HttpSession. timeoutInSeconds is
54 | * optional parameter. If not set, will be defaulted to 24 hours (86400s)
55 | *
56 | * @param timeoutInSeconds
57 | * @param httpSession
58 | * @return
59 | */
60 | @RequestMapping(method = RequestMethod.PUT, value = "/loginsession/timeout")
61 | public @ResponseBody
62 | String setSessionTimeout(
63 | @RequestParam(value = "timeoutInSeconds", defaultValue = "86400") int timeoutInSeconds,
64 | HttpSession httpSession) {
65 | httpSession.setMaxInactiveInterval(timeoutInSeconds);
66 | return "httpSession timeout set to:"
67 | + httpSession.getMaxInactiveInterval();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/dao/SecurityACLDAO.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.dao;
2 |
3 | import java.util.List;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.security.acls.domain.ObjectIdentityImpl;
8 | import org.springframework.security.acls.model.AccessControlEntry;
9 | import org.springframework.security.acls.model.MutableAcl;
10 | import org.springframework.security.acls.model.MutableAclService;
11 | import org.springframework.security.acls.model.NotFoundException;
12 | import org.springframework.security.acls.model.ObjectIdentity;
13 | import org.springframework.security.acls.model.Permission;
14 | import org.springframework.security.acls.model.Sid;
15 | import org.springframework.stereotype.Repository;
16 | import org.springframework.transaction.annotation.Transactional;
17 | import org.springframework.util.Assert;
18 |
19 | import sample.sdr.auth.bean.AbstractSecuredEntity;
20 |
21 | @Repository
22 | public class SecurityACLDAO {
23 | private static Logger logger = LoggerFactory.getLogger(SecurityACLDAO.class);
24 |
25 | private MutableAclService mutableAclService;
26 |
27 | public SecurityACLDAO() {
28 | }
29 |
30 | @Transactional(readOnly = false)
31 | public void addPermission(AbstractSecuredEntity element, Sid recipient, Permission permission) {
32 | MutableAcl acl;
33 | //ObjectIdentity oid = new ObjectIdentityImpl(AbstractSecuredEntity.class, element.getId());
34 | ObjectIdentity oid = new ObjectIdentityImpl(element);
35 |
36 | try {
37 | acl = (MutableAcl) mutableAclService.readAclById(oid);
38 | } catch (NotFoundException nfe) {
39 | acl = mutableAclService.createAcl(oid);
40 | }
41 |
42 | acl.insertAce(acl.getEntries().size(), permission, recipient, true);
43 | mutableAclService.updateAcl(acl);
44 |
45 | logger.debug("Added permission " + permission + " for Sid " + recipient + " contact " + element);
46 | }
47 |
48 | @Transactional(readOnly = false)
49 | public boolean addAccessControlEntry(AbstractSecuredEntity element, Sid recipient, Permission permission) {
50 | Assert.notNull(element, "AbstractSecuredEntity required");
51 | Assert.notNull(recipient, "recipient required");
52 | Assert.notNull(permission, "permission required");
53 |
54 | MutableAcl acl;
55 | //ObjectIdentity oid = new ObjectIdentityImpl(AbstractSecuredEntity.class, element.getId());
56 | ObjectIdentity oid = new ObjectIdentityImpl(element);
57 |
58 | try {
59 | acl = (MutableAcl) mutableAclService.readAclById(oid);
60 | } catch (NotFoundException nfe) {
61 | acl = mutableAclService.createAcl(oid);
62 | }
63 |
64 | /*
65 | * handle duplicate ACL entries
66 | * http://forum.springsource.org/showthread.php?73022-Should-AclImpl-allow-duplicate-permissions
67 | */
68 | if(doesACEExists(element, recipient, permission)) {
69 | logger.debug("ACE already exists, element:" + element.getId() + ", Sid:" + recipient + ", permission:" + permission);
70 | } else {
71 | acl.insertAce(acl.getEntries().size(), permission, recipient, true);
72 | mutableAclService.updateAcl(acl);
73 | logger.debug("Added permission " + permission + " for Sid " + recipient + " contact " + element);
74 | }
75 | return true;
76 | }
77 |
78 | /*
79 | * Check if ACE - Access Control Entry exists already
80 | */
81 | public boolean doesACEExists(AbstractSecuredEntity element, Sid recipient, Permission permission) {
82 | boolean result = false;
83 | ObjectIdentity oid = new ObjectIdentityImpl(element);
84 | MutableAcl acl;
85 | try {
86 | acl = (MutableAcl) mutableAclService.readAclById(oid);
87 | } catch (NotFoundException nfe) {
88 | acl = mutableAclService.createAcl(oid);
89 | }
90 |
91 | List entries = acl.getEntries();
92 | for(AccessControlEntry ace : entries) {
93 | if (ace.getSid().equals(recipient) && ace.getPermission().equals(permission)) {
94 | //result = true;
95 | return true;
96 | }
97 | }
98 | return result;
99 | }
100 |
101 | @Transactional(readOnly = false)
102 | public void deletePermission(AbstractSecuredEntity element) {
103 | // Delete the ACL information as well
104 | //ObjectIdentity oid = new ObjectIdentityImpl(AbstractSecuredEntity.class, element.getId());
105 | ObjectIdentity oid = new ObjectIdentityImpl(element);
106 | mutableAclService.deleteAcl(oid, false);
107 | }
108 |
109 | @Transactional(readOnly = false)
110 | public boolean deleteAccessControlEntry(AbstractSecuredEntity element, Sid recipient, Permission permission) {
111 | ObjectIdentity oid = new ObjectIdentityImpl(element);
112 | MutableAcl acl;
113 | try {
114 | acl = (MutableAcl) mutableAclService.readAclById(oid);
115 | } catch (NotFoundException nfe) {
116 | acl = mutableAclService.createAcl(oid);
117 | }
118 |
119 | List entries = acl.getEntries();
120 | for (int i = 0; i < entries.size(); i++) {
121 | if (entries.get(i).getSid().equals(recipient) && entries.get(i).getPermission().equals(permission)) {
122 | acl.deleteAce(i);
123 | }
124 | }
125 |
126 | mutableAclService.updateAcl(acl);
127 |
128 | if (logger.isDebugEnabled()) {
129 | logger.debug("Deleted Permission:" + permission + " for recipient: "+ recipient + ", for object: " + element);
130 | }
131 |
132 | return true;
133 | }
134 |
135 | public MutableAclService getMutableAclService() {
136 | return mutableAclService;
137 | }
138 |
139 | public void setMutableAclService(MutableAclService mutableAclService) {
140 | this.mutableAclService = mutableAclService;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/dto/UserACLRequest.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.dto;
2 |
3 | import javax.xml.bind.annotation.XmlRootElement;
4 |
5 | @XmlRootElement(name="UserACLRequest")
6 | public class UserACLRequest {
7 | String entityClassName;
8 | String entityId;
9 | String userName;
10 | Integer permission;
11 |
12 | public Integer getPermission() {
13 | return permission;
14 | }
15 | public void setPermission(Integer permission) {
16 | this.permission = permission;
17 | }
18 | public String getEntityClassName() {
19 | return entityClassName;
20 | }
21 | public void setEntityClassName(String entityClassName) {
22 | this.entityClassName = entityClassName;
23 | }
24 | public String getEntityId() {
25 | return entityId;
26 | }
27 | public void setEntityId(String entityId) {
28 | this.entityId = entityId;
29 | }
30 | public String getUserName() {
31 | return userName;
32 | }
33 | public void setUserName(String userName) {
34 | this.userName = userName;
35 | }
36 | @Override
37 | public String toString() {
38 | return "UserACLRequest [entityClassName=" + entityClassName
39 | + ", entityId=" + entityId + ", userName=" + userName
40 | + ", permission=" + permission + "]";
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/dto/UserACLRequestSet.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.dto;
2 |
3 | import java.util.List;
4 |
5 | import javax.xml.bind.annotation.XmlRootElement;
6 |
7 | @XmlRootElement(name="UserACLRequestSet")
8 | public class UserACLRequestSet {
9 | List aclList;
10 |
11 | public List getAclList() {
12 | return aclList;
13 | }
14 |
15 | public void setAclList(List aclList) {
16 | this.aclList = aclList;
17 | }
18 | }
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/log/BookstoreLogger.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.log;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import ch.qos.logback.classic.LoggerContext;
7 | import ch.qos.logback.classic.joran.JoranConfigurator;
8 | import ch.qos.logback.core.joran.spi.JoranException;
9 | import ch.qos.logback.core.util.StatusPrinter;
10 |
11 | public class BookstoreLogger {
12 | private String logbackconfig;
13 | private static Logger logger = LoggerFactory.getLogger(BookstoreLogger.class);
14 |
15 | public BookstoreLogger(String logbackconfig) {
16 | this.logbackconfig = logbackconfig;
17 | init();
18 | }
19 |
20 | private void init() {
21 | JoranConfigurator configurator = new JoranConfigurator();
22 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
23 | configurator.setContext(loggerContext);
24 | loggerContext.reset();
25 | try {
26 | configurator.doConfigure(logbackconfig);
27 | } catch (JoranException e) {
28 | // StatusPrinter will handle this
29 | }
30 | StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext);
31 |
32 | logger.info("Test Log");
33 | logger.error("error msg");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/repo/handlers/BookHandler.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.repo.handlers;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.data.rest.core.annotation.HandleAfterCreate;
7 | import org.springframework.data.rest.core.annotation.HandleAfterDelete;
8 | import org.springframework.data.rest.core.annotation.HandleAfterSave;
9 | import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
10 | import org.springframework.security.acls.domain.BasePermission;
11 | import org.springframework.security.acls.domain.GrantedAuthoritySid;
12 | import org.springframework.security.acls.domain.PrincipalSid;
13 |
14 | import sample.sdr.auth.bean.AbstractSecuredEntity;
15 | import sample.sdr.auth.bean.Book;
16 | import sample.sdr.auth.dao.SecurityACLDAO;
17 | import sample.sdr.auth.security.SecurityUtil;
18 |
19 | @RepositoryEventHandler(Book.class)
20 | public class BookHandler {
21 | private static Logger logger = LoggerFactory.getLogger(BookHandler.class);
22 |
23 | @Autowired
24 | private SecurityACLDAO securityACLDAO;
25 |
26 | @HandleAfterCreate
27 | public void afterCreate(Book book) {
28 | logger.debug("afterCreate:{}", book.toString());
29 | addACL(book);
30 | }
31 |
32 | @HandleAfterSave
33 | public void handleAfterSave(Book book) {
34 | logger.debug("afterSave:{}", book.toString());
35 | }
36 |
37 | @HandleAfterDelete
38 | public void handleAfterDelete(Book book) {
39 | removeACL(book);
40 | }
41 |
42 | private void addACL(AbstractSecuredEntity type) {
43 | if(type != null) {
44 | securityACLDAO.addPermission(type, new PrincipalSid(SecurityUtil.getUsername()), BasePermission.ADMINISTRATION);
45 | securityACLDAO.addPermission(type, new PrincipalSid(SecurityUtil.getUsername()), BasePermission.READ);
46 | securityACLDAO.addPermission(type, new PrincipalSid(SecurityUtil.getUsername()), BasePermission.WRITE);
47 | securityACLDAO.addPermission(type, new PrincipalSid(SecurityUtil.getUsername()), BasePermission.DELETE);
48 |
49 | securityACLDAO.addPermission(type, new GrantedAuthoritySid("ROLE_ADMIN"), BasePermission.ADMINISTRATION);
50 | }
51 | }
52 |
53 | private void removeACL(AbstractSecuredEntity type) {
54 | //TBD
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/repositories/BookRepository.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.repositories;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource;
5 | import org.springframework.security.access.method.P;
6 | import org.springframework.security.access.prepost.PostFilter;
7 | import org.springframework.security.access.prepost.PreAuthorize;
8 |
9 | import sample.sdr.auth.bean.Book;
10 |
11 | @RepositoryRestResource(path = "book")
12 | public interface BookRepository extends CrudRepository {
13 |
14 | @PreAuthorize("hasRole('ROLE_ADMIN') or hasPermission(#book, 'write')")
15 | Book save(@P("book") Book book);
16 |
17 | @Override
18 | @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, admin)")
19 | Iterable findAll();
20 | }
21 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/repositories/RoleEntityRepo.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.repositories;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource;
5 |
6 | import sample.sdr.auth.bean.RoleEntity;
7 |
8 | @RepositoryRestResource(path = "role")
9 | public interface RoleEntityRepo extends JpaRepository {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/repositories/UserEntityRepo.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.repositories;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource;
5 |
6 | import sample.sdr.auth.bean.UserEntity;
7 |
8 | @RepositoryRestResource(path = "user")
9 | public interface UserEntityRepo extends JpaRepository {
10 |
11 | UserEntity findByUsername(String username);
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/security/CustomUserDetailsService.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.security;
2 |
3 | import java.io.Serializable;
4 | import java.util.ArrayList;
5 | import java.util.Collection;
6 | import java.util.List;
7 |
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.security.core.GrantedAuthority;
12 | import org.springframework.security.core.userdetails.User;
13 | import org.springframework.security.core.userdetails.UserDetails;
14 | import org.springframework.security.core.userdetails.UserDetailsService;
15 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
16 |
17 | import sample.sdr.auth.bean.RoleEntity;
18 | import sample.sdr.auth.bean.UserEntity;
19 | import sample.sdr.auth.repositories.UserEntityRepo;
20 |
21 | public class CustomUserDetailsService implements Serializable,
22 | UserDetailsService {
23 |
24 | private static final long serialVersionUID = 1L;
25 |
26 | private static Logger logger = LoggerFactory
27 | .getLogger(CustomUserDetailsService.class);
28 |
29 | @Autowired
30 | private transient UserEntityRepo userEntityRepo;
31 |
32 | /**
33 | * Returns a populated {@link UserDetails} object.
34 | * The username is first retrieved from the database and then mapped to
35 | * a {@link UserDetails} object.
36 | */
37 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
38 | try {
39 | UserEntity user = userEntityRepo.findByUsername(username);
40 |
41 | boolean enabled = true;
42 | boolean accountNonExpired = true;
43 | boolean credentialsNonExpired = true;
44 | boolean accountNonLocked = true;
45 |
46 | return new User(
47 | user.getUsername(),
48 | user.getPassword(),
49 | enabled,
50 | accountNonExpired,
51 | credentialsNonExpired,
52 | accountNonLocked,
53 | getAuthorities(user.getUsername()));
54 | } catch (Exception e) {
55 | throw new RuntimeException(e);
56 | }
57 | }
58 |
59 | private Collection getAuthorities(String userName) {
60 | logger.debug("in getAuthoritiesFromDb");
61 | List l = new ArrayList();
62 |
63 | UserEntity user = userEntityRepo.findByUsername(userName);
64 | if(user == null) {
65 | return l;
66 | }
67 | for(final RoleEntity role : user.getRoles()) {
68 | l.add( new GrantedAuthority() {
69 | private static final long serialVersionUID = 1L;
70 |
71 | @Override
72 | public String getAuthority() {
73 | return role.getAuthority();
74 | }
75 |
76 | @Override
77 | public String toString() {
78 | return getAuthority();
79 | }
80 | });
81 | }
82 |
83 | return l;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/security/SecurityUtil.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.security;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.security.core.Authentication;
9 | import org.springframework.security.core.GrantedAuthority;
10 | import org.springframework.security.core.context.SecurityContextHolder;
11 | import org.springframework.security.core.userdetails.UserDetails;
12 |
13 | public class SecurityUtil {
14 | private static Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
15 |
16 | public static String getUsername() {
17 | Authentication auth = SecurityContextHolder.getContext().getAuthentication();
18 |
19 | if (auth.getPrincipal() instanceof UserDetails) {
20 | return ((UserDetails) auth.getPrincipal()).getUsername();
21 | } else {
22 | return auth.getPrincipal().toString();
23 | }
24 | }
25 |
26 | public static ArrayList getUserRoles() {
27 | Authentication auth = SecurityContextHolder.getContext()
28 | .getAuthentication();
29 | Collection extends GrantedAuthority> authorities = auth
30 | .getAuthorities();
31 | ArrayList currentUserRoles = new ArrayList();
32 | for (GrantedAuthority authority : authorities) {
33 | currentUserRoles.add(authority.getAuthority());
34 | }
35 | if (logger.isDebugEnabled()) {
36 | logger.debug("currentUserRoles:" + currentUserRoles);
37 | }
38 | return currentUserRoles;
39 | }
40 |
41 | public static boolean doesUserHasRole(String role) {
42 | ArrayList currentUserRoles = getUserRoles();
43 | for(String s : currentUserRoles) {
44 | if(s.equals(role)) {
45 | return true;
46 | }
47 | }
48 | return false;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/bookstore/src/main/java/sample/sdr/auth/util/Constants.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.util;
2 |
3 |
4 | public final class Constants {
5 |
6 | public static final String USERENTITY_REPO = "userEntityRepo";
7 | public static final String SECURITYACL_DAO = "securityACLDAO";
8 | public static final String BOOK_REPOSITORY = "bookRepository";
9 |
10 | private Constants() {
11 | throw new AssertionError();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/bookstore/src/main/resources/META-INF/spring-data-rest/repositories-export.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/bookstore/src/main/resources/ehcache-hibernate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
11 |
12 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/bookstore/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
8 | /var/log/bookstore/bookstore.log
9 |
10 |
11 | /var/log/bookstore/bookstore-%d{yyyy-MM-dd}.%i.gz
12 |
13 |
15 |
16 | 5MB
17 |
18 |
19 |
20 | %d [%t] %-5p %logger{0} - %m%n
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-auth-config.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-auth-rules.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-context.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
27 |
28 |
29 |
30 | ${logback.configloc}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-dao.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-dbconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
21 | ${jdbc.dbserver}
22 | ${jdbc.dbport}
23 | ${jdbc.dbname}
24 | ${jdbc.username}
25 | ${jdbc.password}
26 | ${pgjdbc-ng.housekeeper}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-jpaconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${hibernate.dialect}
27 | update
28 | ${hibernate.show_sql}
29 |
30 | org.hibernate.cache.ehcache.EhCacheRegionFactory
31 | true
32 | true
33 | true
34 | ${hibernate.cacheconfig}
35 |
36 | true
37 | org.hibernate.envers.strategy.ValidityAuditStrategy
38 | true
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-sdr-auth.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-security.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
18 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
45 |
46 |
47 |
48 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-servlet.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
35 |
36 |
37 |
38 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-spring-data-rest.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
11 |
12 |
13 |
14 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-test-context.xml:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/bookstore-test-servlet.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
17 |
18 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
36 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bookstore/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Bookstore
7 |
8 |
9 | contextConfigLocation
10 |
11 | /WEB-INF/bookstore-context.xml
12 | /WEB-INF/bookstore-security.xml
13 | /WEB-INF/bookstore-auth-config.xml
14 |
15 |
16 |
17 |
18 | springSecurityFilterChain
19 | org.springframework.web.filter.DelegatingFilterProxy
20 |
21 |
22 |
23 | springSecurityFilterChain
24 | /*
25 |
26 |
27 |
28 | org.springframework.web.context.ContextLoaderListener
29 |
30 |
31 |
32 |
33 | org.springframework.security.web.session.HttpSessionEventPublisher
34 |
35 |
36 |
37 |
38 |
39 | org.springframework.web.context.request.RequestContextListener
40 |
41 |
42 |
43 |
44 | ViewStatusMessages
45 | ch.qos.logback.classic.ViewStatusMessagesServlet
46 |
47 |
48 | ViewStatusMessages
49 | /lbClassicStatus
50 |
51 |
52 |
53 | httpPutFormFilter
54 | org.springframework.web.filter.HttpPutFormContentFilter
55 |
56 |
57 |
58 | httpPutFormFilter
59 | bookstore
60 |
61 |
62 |
63 | bookstore
64 | org.springframework.web.servlet.DispatcherServlet
65 | 1
66 |
67 | 52428800
68 | 52428800
69 | 0
70 |
71 |
72 |
73 |
74 | bookstore
75 | /
76 |
77 |
78 |
--------------------------------------------------------------------------------
/bookstore/src/scripts/sql/bookstoreConfig.sql:
--------------------------------------------------------------------------------
1 | -- default admin user, pwd: admin123
2 | INSERT INTO userentity(id, enabled, password, username) VALUES(nextval('hibernate_sequence'),'true','$2a$10$Isti6gH/65twVovOwzDz5eryiJeRv3OLPwsihq9lTcij5UG/wIiVO','admin');
3 |
4 | INSERT INTO roleentity(id, authority) VALUES(nextval('hibernate_sequence'),'ROLE_ADMIN');
5 |
6 | INSERT INTO userentity_roleentity(users_id, roles_id) VALUES(1,2);
7 |
--------------------------------------------------------------------------------
/bookstore/src/scripts/sql/createAclSchemaPostgres.sql:
--------------------------------------------------------------------------------
1 | -- ACL Schema SQL for PostgreSQL
2 |
3 | -- drop table acl_entry;
4 | -- drop table acl_object_identity;
5 | -- drop table acl_class;
6 | -- drop table acl_sid;
7 |
8 | create table acl_sid(
9 | id bigserial not null primary key,
10 | principal boolean not null,
11 | sid varchar(100) not null,
12 | constraint unique_uk_1 unique(sid,principal)
13 | );
14 |
15 | ALTER TABLE public.acl_sid OWNER TO book;
16 |
17 | create table acl_class(
18 | id bigserial not null primary key,
19 | class varchar(100) not null,
20 | constraint unique_uk_2 unique(class)
21 | );
22 |
23 | ALTER TABLE public.acl_class OWNER TO book;
24 |
25 | create table acl_object_identity(
26 | id bigserial primary key,
27 | object_id_class bigint not null,
28 | object_id_identity bigint not null,
29 | parent_object bigint,
30 | owner_sid bigint,
31 | entries_inheriting boolean not null,
32 | constraint unique_uk_3 unique(object_id_class,object_id_identity),
33 | constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),
34 | constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),
35 | constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)
36 | );
37 |
38 | ALTER TABLE public.acl_object_identity OWNER TO book;
39 |
40 | create table acl_entry(
41 | id bigserial primary key,
42 | acl_object_identity bigint not null,
43 | ace_order int not null,
44 | sid bigint not null,
45 | mask integer not null,
46 | granting boolean not null,
47 | audit_success boolean not null,
48 | audit_failure boolean not null,
49 | constraint unique_uk_4 unique(acl_object_identity,ace_order),
50 | constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),
51 | constraint foreign_fk_5 foreign key(sid) references acl_sid(id)
52 | );
53 |
54 | ALTER TABLE public.acl_entry OWNER TO book;
55 |
56 |
--------------------------------------------------------------------------------
/bookstore/src/test/curl/bookstorecurl.txt:
--------------------------------------------------------------------------------
1 | To discover what REST apis are available, issue an HTTP GET to the root URL:
2 | curl --user admin:admin123 -i -k -X GET 'http://localhost:8080/bookstore/'
3 |
4 | output:
5 | HTTP/1.1 200 OK
6 | Server: Apache-Coyote/1.1
7 | Set-Cookie: JSESSIONID=9865FFD13A04FE1D9D26BA0726533308; Path=/bookstore/; HttpOnly
8 | Content-Type: application/hal+json
9 | Transfer-Encoding: chunked
10 | Date: Sat, 25 Oct 2014 19:30:18 GMT
11 |
12 | {
13 | "_links" : {
14 | "books" : {
15 | "href" : "http://localhost:8080/bookstore/book"
16 | },
17 | "roleEntities" : {
18 | "href" : "http://localhost:8080/bookstore/role{?page,size,sort}",
19 | "templated" : true
20 | },
21 | "userEntities" : {
22 | "href" : "http://localhost:8080/bookstore/user{?page,size,sort}",
23 | "templated" : true
24 | }
25 | }
26 | }
27 |
28 |
29 | Create a book
30 | curl --user admin:admin123 -i -k -X POST -Hcontent-type:application/json -HAccept:application/json --data '{"bookName" : "One Straw Revolution","description" : "about natural farming","author" : "Masanobu Fukuoka" }' http://localhost:8080/bookstore/book/
31 |
32 | output:
33 | HTTP/1.1 201 Created
34 | Server: Apache-Coyote/1.1
35 | Set-Cookie: JSESSIONID=B59C34DFA3897EEDA215B92A335BB739; Path=/bookstore/; HttpOnly
36 | Location: http://localhost:8080/bookstore/book/3
37 | Content-Length: 0
38 | Date: Sat, 25 Oct 2014 19:37:14 GMT
39 |
40 |
41 | Create a role:
42 | curl --user admin:admin123 -i -k -X POST -Hcontent-type:application/json -HAccept:application/json --data '{"authority" : "ROLE_USER"}' http://localhost:8080/bookstore/role/
43 |
44 | output:
45 | HTTP/1.1 201 Created
46 | Server: Apache-Coyote/1.1
47 | Set-Cookie: JSESSIONID=76DA36AE24B65817448DC75DE9DE1AA8; Path=/bookstore/; HttpOnly
48 | Location: http://localhost:8080/bookstore/role/5
49 | Content-Length: 0
50 | Date: Sat, 25 Oct 2014 19:42:26 GMT
51 |
52 |
53 | Create a user
54 | curl --user admin:admin123 -i -k -X POST -Hcontent-type:application/json -HAccept:application/json --data '{"salutation" : "Mr.", "firstName" : "Bob", "lastName" : "Jr", "email" : "bob.jr@bookstore.com", "mobile" : null, "username" : "bob", "password" : "bob123", "enabled" : true, "roles" : [ "http://localhost:8080/bookstore/role/5"]}' http://localhost:8080/bookstore/user/
55 |
56 | output:
57 | HTTP/1.1 201 Created
58 | Server: Apache-Coyote/1.1
59 | Set-Cookie: JSESSIONID=A1BF733A46B4250383190F58E42F05D8; Path=/bookstore/; HttpOnly
60 | Location: http://localhost:8080/bookstore/user/6
61 | Content-Length: 0
62 | Date: Sat, 25 Oct 2014 19:48:54 GMT
63 |
64 | Fetch books visible to admin:
65 | curl --user admin:admin123 -i -k -X GET 'http://localhost:8080/bookstore/book'
66 |
67 | output:
68 | HTTP/1.1 200 OK
69 | Server: Apache-Coyote/1.1
70 | Set-Cookie: JSESSIONID=87AD10C3612E605B8BF9E9AF3B457FF2; Path=/bookstore/; HttpOnly
71 | Content-Type: application/hal+json
72 | Transfer-Encoding: chunked
73 | Date: Sat, 25 Oct 2014 19:49:59 GMT
74 |
75 | {
76 | "_embedded" : {
77 | "books" : [ {
78 | "bookName" : "One Straw Revolution",
79 | "description" : "about natural farming",
80 | "author" : "Masanobu Fukuoka",
81 | "_links" : {
82 | "self" : {
83 | "href" : "http://localhost:8080/bookstore/book/3"
84 | }
85 | }
86 | } ]
87 | }
88 | }
89 |
90 | Fetch books visible to user bob:
91 | curl --user bob:bob123 -i -k -X GET 'http://localhost:8080/bookstore/book'
92 |
93 | output:
94 | HTTP/1.1 200 OK
95 | Server: Apache-Coyote/1.1
96 | Set-Cookie: JSESSIONID=3174E89956407679E50BF135D38A58F6; Path=/bookstore/; HttpOnly
97 | Content-Type: application/hal+json
98 | Transfer-Encoding: chunked
99 | Date: Sat, 25 Oct 2014 19:51:15 GMT
100 |
101 | { }
102 |
103 | Provide READ permission to bob:
104 | curl --user admin:admin123 -k -X POST -Hcontent-type:application/json -HAccept:application/json --data '{"aclList":[{"entityClassName":"Book","entityId":3,"userName": "bob","permission":0}]}' http://localhost:8080/bookstore/acl/user/generic/
105 |
106 | output:
107 | ["Successfully added all ACLs"]
108 |
109 | Now fetch books visible to user bob:
110 | curl --user bob:bob123 -i -k -X GET 'http://localhost:8080/bookstore/book'
111 |
112 | output:
113 | HTTP/1.1 200 OK
114 | Server: Apache-Coyote/1.1
115 | Set-Cookie: JSESSIONID=79624CC74339FC56C0234982273CDD6D; Path=/bookstore/; HttpOnly
116 | Content-Type: application/hal+json
117 | Transfer-Encoding: chunked
118 | Date: Sat, 25 Oct 2014 21:06:43 GMT
119 |
120 | {
121 | "_embedded" : {
122 | "books" : [ {
123 | "bookName" : "One Straw Revolution",
124 | "description" : "about natural farming",
125 | "author" : "Masanobu Fukuoka",
126 | "_links" : {
127 | "self" : {
128 | "href" : "http://localhost:8080/bookstore/book/3"
129 | }
130 | }
131 | } ]
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/bookstore/src/test/java/sample/sdr/auth/test/api/TestBookCRUD.java:
--------------------------------------------------------------------------------
1 | package sample.sdr.auth.test.api;
2 |
3 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
4 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
5 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
6 | import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
7 |
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.http.MediaType;
13 | import org.springframework.test.context.ContextConfiguration;
14 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15 | import org.springframework.test.context.web.WebAppConfiguration;
16 | import org.springframework.test.web.servlet.MockMvc;
17 | import org.springframework.web.context.WebApplicationContext;
18 |
19 | @RunWith(SpringJUnit4ClassRunner.class)
20 | @WebAppConfiguration
21 | @ContextConfiguration("file:src/main/webapp/WEB-INF/bookstore-test-context.xml")
22 | public class TestBookCRUD {
23 |
24 | @Autowired
25 | private WebApplicationContext wac;
26 |
27 | private MockMvc mockMvc;
28 |
29 | @Before
30 | public void setup() {
31 | this.mockMvc = webAppContextSetup(this.wac).build();
32 | }
33 |
34 | @Test
35 | public void getFoo() throws Exception {
36 | this.mockMvc.perform(get("/foo").accept(MediaType.APPLICATION_JSON))
37 | .andExpect(status().isNotFound());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/bookstore/src/test/resources/bookstore-test.properties:
--------------------------------------------------------------------------------
1 | # not required as logback-test.xml gets picked up by default during unit-testing
2 | #logback.configloc=
3 |
4 | jdbc.dataSourceClassName=org.hsqldb.jdbc.jdbcDataSource
5 | jdbc.dbserver=localhost
6 | jdbc.dbport=5432
7 | jdbc.dbname=bookstore
8 | jdbc.username=book
9 | jdbc.password=book
10 |
11 | hibernate.dialect=org.hibernate.dialect.HSQLDialect
12 | hibernate.show_sql=false
13 | hibernate.cacheconfig=/ehcache-hibernate-test.xml
14 |
15 | #hikaricp properties - connection pooling
16 | hikari.maxPoolSize=20
17 | pgjdbc-ng.housekeeper=false
18 |
--------------------------------------------------------------------------------
/bookstore/src/test/resources/ehcache-hibernate-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
11 |
12 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/bookstore/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
8 | /tmp/bookstore.log
9 |
10 |
11 | /tmp/bookstore-%d{yyyy-MM-dd}.%i.gz
12 |
13 |
15 |
16 | 5MB
17 |
18 |
19 |
20 | %d [%t] %-5p %logger{0} - %m%n
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------