rsanchez
2017-04-10 8d99c88af55041ff06e6b9372b6b1f66220bed38
#3529 feature - Added applications to user profile and upgrade to
angular4
16 files modified
changed files
securis/src/main/java/net/curisit/securis/db/Application.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/Pack.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/User.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/security/BasicSecurityContext.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/services/PackResource.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/services/UserResource.java patch | view | blame | history
securis/src/main/resources/db/schema.sql patch | view | blame | history
securis/src/main/webapp/index.html patch | view | blame | history
securis/src/main/webapp/package.json patch | view | blame | history
securis/src/main/webapp/sql_update.sql patch | view | blame | history
securis/src/main/webapp/src/app/app.module.ts patch | view | blame | history
securis/src/main/webapp/src/app/forms/user.form.component.ts patch | view | blame | history
securis/src/main/webapp/src/app/forms/user.form.html patch | view | blame | history
securis/src/main/webapp/src/app/listing/user.list.component.html patch | view | blame | history
securis/src/main/webapp/systemjs.config.js patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/Application.java
....@@ -10,6 +10,9 @@
1010 import javax.persistence.FetchType;
1111 import javax.persistence.GeneratedValue;
1212 import javax.persistence.Id;
13
+import javax.persistence.JoinColumn;
14
+import javax.persistence.JoinTable;
15
+import javax.persistence.ManyToMany;
1316 import javax.persistence.NamedQueries;
1417 import javax.persistence.NamedQuery;
1518 import javax.persistence.OneToMany;
....@@ -68,6 +71,14 @@
6871 @JsonManagedReference
6972 private Set<ApplicationMetadata> metadata;
7073
74
+ @JsonIgnore
75
+ // We don't include the users to limit the size of each row a the listing
76
+ @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
77
+ @JoinTable(name = "user_application", //
78
+ joinColumns = { @JoinColumn(name = "application_id", referencedColumnName = "id") }, //
79
+ inverseJoinColumns = { @JoinColumn(name = "username", referencedColumnName = "username") })
80
+ private Set<User> users;
81
+
7182 public Integer getId() {
7283 return id;
7384 }
securis/src/main/java/net/curisit/securis/db/Pack.java
....@@ -43,6 +43,7 @@
4343 @NamedQueries({ @NamedQuery(name = "list-packs", query = "SELECT pa FROM Pack pa"), //
4444 @NamedQuery(name = "pack-by-code", query = "SELECT pa FROM Pack pa where pa.code = :code"), //
4545 @NamedQuery(name = "list-packs-by-lic-type", query = "SELECT pa FROM Pack pa where pa.licenseType.id = :lt_id"), //
46
+ @NamedQuery(name = "list-packs-by-orgs-apps", query = "SELECT pa FROM Pack pa where pa.organization.id in :list_ids_org and pa.licenseType.application.id in :list_ids_app "), //
4647 @NamedQuery(name = "list-packs-by-orgs", query = "SELECT pa FROM Pack pa where pa.organization.id in :list_ids") })
4748 public class Pack implements Serializable {
4849
securis/src/main/java/net/curisit/securis/db/User.java
....@@ -2,10 +2,12 @@
22
33 import java.io.Serializable;
44 import java.util.ArrayList;
5
+import java.util.Collection;
56 import java.util.Date;
67 import java.util.HashSet;
78 import java.util.List;
89 import java.util.Set;
10
+import java.util.stream.Collectors;
911
1012 import javax.persistence.Column;
1113 import javax.persistence.Entity;
....@@ -76,6 +78,14 @@
7678 inverseJoinColumns = { @JoinColumn(name = "organization_id", referencedColumnName = "id") } //
7779 )
7880 private Set<Organization> organizations;
81
+
82
+ @JsonIgnore
83
+ @ManyToMany
84
+ @JoinTable(name = "user_application", //
85
+ joinColumns = { @JoinColumn(name = "username", referencedColumnName = "username") }, //
86
+ inverseJoinColumns = { @JoinColumn(name = "application_id", referencedColumnName = "id") } //
87
+ )
88
+ private Set<Application> applications;
7989
8090 public String getUsername() {
8191 return username;
....@@ -182,6 +192,14 @@
182192 this.organizations = organizations;
183193 }
184194
195
+ public Set<Application> getApplications() {
196
+ return applications;
197
+ }
198
+
199
+ public void setApplications(Set<Application> applications) {
200
+ this.applications = applications;
201
+ }
202
+
185203 @JsonProperty("organizations_ids")
186204 public void setOrgsIds(List<Integer> orgsIds) {
187205 organizations = new HashSet<>();
....@@ -204,6 +222,28 @@
204222 return ids;
205223 }
206224
225
+ @JsonProperty("applications_ids")
226
+ public void setAppsIds(Collection<Integer> appIds) {
227
+ applications = new HashSet<>();
228
+ for (Integer appid : appIds) {
229
+ Application a = new Application();
230
+ a.setId(appid);
231
+ applications.add(a);
232
+ }
233
+ }
234
+
235
+ @JsonProperty("applications_ids")
236
+ public Set<Integer> getAppsIds() {
237
+ if (applications == null) {
238
+ return null;
239
+ }
240
+ Set<Integer> ids = new HashSet<>();
241
+ for (Application app : applications) {
242
+ ids.add(app.getId());
243
+ }
244
+ return ids;
245
+ }
246
+
207247 @JsonIgnore
208248 public Set<Integer> getAllOrgsIds() {
209249 if (organizations == null) {
....@@ -214,6 +254,22 @@
214254 return ids;
215255 }
216256
257
+ @JsonIgnore
258
+ public Set<Integer> getAllAppsIds() {
259
+ if (applications == null) {
260
+ return null;
261
+ }
262
+ Set<Integer> ids = this.applications.parallelStream().map(app -> app.getId()).collect(Collectors.toSet());
263
+
264
+ return ids;
265
+ }
266
+
267
+ /**
268
+ * Walk into the organization hierarchy to include all descendants
269
+ *
270
+ * @param list
271
+ * @param orgIds
272
+ */
217273 private void includeAllOrgs(Set<Organization> list, Set<Integer> orgIds) {
218274 for (Organization org : list) {
219275 orgIds.add(org.getId());
....@@ -229,10 +285,16 @@
229285 this.email = email;
230286 }
231287
288
+ /**
289
+ * Numeric rol mask. Be aware to use different bit position for each role
290
+ *
291
+ * @author rob
292
+ */
232293 public static class Rol {
233294 public static final int ADVANCE = 0x01;
234295 public static final int ADMIN = 0x02;
235
- public static final int[] ALL = new int[] { ADVANCE, ADMIN };
296
+ public static final int BASIC = 0x04;
297
+ public static final int[] ALL = new int[] { ADVANCE, ADMIN, BASIC };
236298 }
237299
238300 }
securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
....@@ -21,12 +21,6 @@
2121 import javax.ws.rs.ext.WriterInterceptor;
2222 import javax.ws.rs.ext.WriterInterceptorContext;
2323
24
-import net.curisit.securis.db.User;
25
-import net.curisit.securis.security.BasicSecurityContext;
26
-import net.curisit.securis.security.Securable;
27
-import net.curisit.securis.utils.CacheTTL;
28
-import net.curisit.securis.utils.TokenHelper;
29
-
3024 import org.apache.logging.log4j.LogManager;
3125 import org.apache.logging.log4j.Logger;
3226 import org.jboss.resteasy.core.Dispatcher;
....@@ -36,158 +30,188 @@
3630 import org.jboss.resteasy.spi.HttpRequest;
3731 import org.jboss.resteasy.spi.ResteasyProviderFactory;
3832
33
+import net.curisit.securis.db.User;
34
+import net.curisit.securis.security.BasicSecurityContext;
35
+import net.curisit.securis.security.Securable;
36
+import net.curisit.securis.utils.CacheTTL;
37
+import net.curisit.securis.utils.TokenHelper;
38
+
3939 @Provider
4040 @Priority(Priorities.AUTHENTICATION)
4141 public class RequestsInterceptor implements ContainerRequestFilter, WriterInterceptor {
42
- private static final Logger LOG = LogManager.getLogger(RequestsInterceptor.class);
42
+ private static final Logger LOG = LogManager.getLogger(RequestsInterceptor.class);
4343
44
- @Context
45
- private HttpServletResponse servletResponse;
44
+ @Context
45
+ private HttpServletResponse servletResponse;
4646
47
- @Context
48
- private HttpServletRequest servletRequest;
47
+ @Context
48
+ private HttpServletRequest servletRequest;
4949
50
- @Inject
51
- private CacheTTL cache;
50
+ @Inject
51
+ private CacheTTL cache;
5252
53
- @Inject
54
- private TokenHelper tokenHelper;
53
+ @Inject
54
+ private TokenHelper tokenHelper;
5555
56
- @Context
57
- private Dispatcher dispatcher;
56
+ @Context
57
+ private Dispatcher dispatcher;
5858
59
- @Inject
60
- private EntityManagerProvider emProvider;
59
+ @Inject
60
+ private EntityManagerProvider emProvider;
6161
62
- @Override
63
- public void filter(ContainerRequestContext containerRequestContext) throws IOException {
64
- EntityManager em = emProvider.getEntityManager();
65
- LOG.debug("GETTING EM: {}", em);
62
+ @Override
63
+ public void filter(ContainerRequestContext containerRequestContext) throws IOException {
64
+ EntityManager em = emProvider.getEntityManager();
65
+ LOG.debug("GETTING EM: {}", em);
6666
67
- ResteasyProviderFactory.pushContext(EntityManager.class, em);
67
+ ResteasyProviderFactory.pushContext(EntityManager.class, em);
6868
69
- ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) containerRequestContext
70
- .getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
71
- Method method = methodInvoker.getMethod();
69
+ ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) containerRequestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
70
+ Method method = methodInvoker.getMethod();
7271
73
- LOG.debug("Stored in context, em: {}, {}", em, method.toGenericString());
72
+ LOG.debug("Stored in context, em: {}, {}", em, method.toGenericString());
7473
75
- boolean next = checkSecurableMethods(containerRequestContext, method);
76
- if (next) {
77
- prepareTransaction(containerRequestContext, method, em);
78
- }
79
- }
74
+ boolean next = checkSecurableMethods(containerRequestContext, method);
75
+ if (next) {
76
+ prepareTransaction(containerRequestContext, method, em);
77
+ }
78
+ }
8079
81
- private void prepareTransaction(ContainerRequestContext containerRequestContext, Method method, EntityManager em) {
80
+ private void prepareTransaction(ContainerRequestContext containerRequestContext, Method method, EntityManager em) {
8281
83
- if (method.isAnnotationPresent(EnsureTransaction.class)) {
84
- LOG.debug("Beginning a new transaction");
85
- em.getTransaction().begin();
86
- }
87
- }
82
+ if (method.isAnnotationPresent(EnsureTransaction.class)) {
83
+ LOG.debug("Beginning a new transaction");
84
+ em.getTransaction().begin();
85
+ }
86
+ }
8887
89
- private boolean checkSecurableMethods(ContainerRequestContext containerRequestContext, Method method) {
90
- if (!method.isAnnotationPresent(Securable.class)) {
91
- return true;
92
- }
93
- String token = servletRequest.getHeader(TokenHelper.TOKEN_HEADER_PÀRAM);
94
- if (token == null || !tokenHelper.isTokenValid(token)) {
95
- LOG.warn("Access denied, Token not valid: {} for method: {}::{}", token, method.getDeclaringClass(), method.getName());
96
- containerRequestContext.abortWith(Response.status(Status.UNAUTHORIZED).build());
97
- return false;
98
- } else {
88
+ private boolean checkSecurableMethods(ContainerRequestContext containerRequestContext, Method method) {
89
+ if (!method.isAnnotationPresent(Securable.class)) {
90
+ return true;
91
+ }
92
+ String token = servletRequest.getHeader(TokenHelper.TOKEN_HEADER_PÀRAM);
93
+ if (token == null || !tokenHelper.isTokenValid(token)) {
94
+ LOG.warn("Access denied, Token not valid: {} for method: {}::{}", token, method.getDeclaringClass(), method.getName());
95
+ containerRequestContext.abortWith(Response.status(Status.UNAUTHORIZED).build());
96
+ return false;
97
+ } else {
98
+ Securable securable = method.getAnnotation(Securable.class);
99
+ // If roles == 0 we only need to validate the token
100
+ String username = tokenHelper.extractUserFromToken(token);
101
+ int userRoles = getUserRoles(username);
102
+ if (securable.roles() != 0 && (securable.roles() & userRoles) == 0) {
103
+ LOG.warn("Method {} requires roles: {}, but user {} hasn't got them", method.getName(), securable.roles(), username);
104
+ containerRequestContext.abortWith(Response.status(Status.UNAUTHORIZED).build());
105
+ return false;
106
+ }
107
+ Set<Integer> orgs = getUserOrganizations(username);
108
+ Set<Integer> apps = getUserApplications(username);
99109
100
- // If roles == 0 we only need to validate the token
101
- String username = tokenHelper.extractUserFromToken(token);
102
- int userRoles = getUserRoles(username);
103
- Set<Integer> orgs = getUserOrganizations(username);
110
+ BasicSecurityContext scw = new BasicSecurityContext(username, userRoles, servletRequest.isSecure());
111
+ scw.setOrganizationsIds(orgs);
112
+ scw.setApplicationsIds(apps);
113
+ containerRequestContext.setSecurityContext(scw);
114
+ // Next line provide injection in resource methods
115
+ ResteasyProviderFactory.pushContext(BasicSecurityContext.class, scw);
116
+ LOG.debug("Added custom SecurityContext for user {}, orgs: {}", username, orgs);
117
+ }
118
+ return true;
104119
105
- BasicSecurityContext scw = new BasicSecurityContext(username, userRoles, servletRequest.isSecure());
106
- scw.setOrganizationsIds(orgs);
107
- containerRequestContext.setSecurityContext(scw);
108
- // Next line provide injection in resource methods
109
- ResteasyProviderFactory.pushContext(BasicSecurityContext.class, scw);
110
- LOG.debug("Added custom SecurityContext for user {}, orgs: {}", username, orgs);
111
- }
112
- return true;
120
+ }
113121
114
- }
122
+ private Set<Integer> getUserOrganizations(String username) {
123
+ @SuppressWarnings("unchecked")
124
+ Set<Integer> userOrgs = cache.get("orgs_" + username, Set.class);
125
+ if (userOrgs == null) {
126
+ EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
115127
116
- private Set<Integer> getUserOrganizations(String username) {
117
- @SuppressWarnings("unchecked")
118
- Set<Integer> userOrgs = cache.get("orgs_" + username, Set.class);
119
- if (userOrgs == null) {
120
- EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
128
+ // Theorically this shouldn't be never null, but just in case...
129
+ User user = em.find(User.class, username);
130
+ if (user != null) {
131
+ userOrgs = user.getAllOrgsIds();
132
+ // We store user orgs in cache only for one hour
133
+ cache.set("orgs_" + username, userOrgs, 3600);
134
+ }
135
+ }
121136
122
- // Theorically this shouldn't be never null, but just in case...
123
- User user = em.find(User.class, username);
124
- if (user != null) {
125
- userOrgs = user.getAllOrgsIds();
126
- // We store user orgs in cache only for one hour
127
- cache.set("orgs_" + username, userOrgs, 3600);
128
- }
129
- }
137
+ return userOrgs;
138
+ }
130139
131
- return userOrgs;
132
- }
140
+ private Set<Integer> getUserApplications(String username) {
141
+ @SuppressWarnings("unchecked")
142
+ Set<Integer> userApps = cache.get("apps_" + username, Set.class);
143
+ if (userApps == null) {
144
+ EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
133145
134
- private int getUserRoles(String username) {
135
- if (username == null) {
136
- return 0;
137
- }
138
- Integer userRoles = cache.get("roles_" + username, Integer.class);
139
- if (userRoles == null) {
140
- EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
146
+ // Theorically this shouldn't be never null, but just in case...
147
+ User user = em.find(User.class, username);
148
+ if (user != null) {
149
+ userApps = user.getAllAppsIds();
150
+ // We store user orgs in cache only for one hour
151
+ cache.set("apps_" + username, userApps, 3600);
152
+ }
153
+ }
141154
142
- User user = em.find(User.class, username);
143
- if (user != null) {
144
- userRoles = 0;
145
- List<Integer> roles = user.getRoles();
146
- if (roles != null) {
147
- for (Integer rol : roles) {
148
- userRoles += rol;
149
- }
150
- }
151
- // We store user roles in cache only for one hour
152
- cache.set("roles_" + username, userRoles, 3600);
153
- cache.set("orgs_" + username, user.getOrgsIds(), 3600);
154
- }
155
- }
156
- return userRoles == null ? 0 : userRoles.intValue();
157
- }
155
+ return userApps;
156
+ }
158157
159
- // @Override
160
- public ServerResponse preProcess(HttpRequest request, ResourceMethodInvoker method) throws Failure, WebApplicationException {
161
- return null;
162
- }
158
+ private int getUserRoles(String username) {
159
+ if (username == null) {
160
+ return 0;
161
+ }
162
+ Integer userRoles = cache.get("roles_" + username, Integer.class);
163
+ if (userRoles == null) {
164
+ EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
163165
164
- @Override
165
- public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
166
- context.proceed();
167
- EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
168
- try {
169
- if (em != null && em.getTransaction().isActive()) {
170
- if (servletResponse.getStatus() == Status.OK.getStatusCode()) {
171
- em.getTransaction().commit();
172
- LOG.debug("COMMIT");
173
- } else {
174
- // This code is never executed if there is an error the
175
- // filter chain is broken
176
- em.getTransaction().rollback();
177
- LOG.debug("ROLLBACK");
178
- }
179
- }
180
- } finally {
181
- if (em.isOpen()) {
182
- LOG.debug("CLOSING EM: {}, trans: {}", em, em.isJoinedToTransaction());
183
- try {
184
- em.close();
185
- } catch (Exception ex) {
186
- ex.printStackTrace();
187
- LOG.error("Error closing EM: {}, {}", em, ex);
188
- }
189
- }
190
- }
191
- }
166
+ User user = em.find(User.class, username);
167
+ if (user != null) {
168
+ userRoles = 0;
169
+ List<Integer> roles = user.getRoles();
170
+ if (roles != null) {
171
+ for (Integer rol : roles) {
172
+ userRoles += rol;
173
+ }
174
+ }
175
+ // We store user roles in cache only for one hour
176
+ cache.set("roles_" + username, userRoles, 3600);
177
+ cache.set("orgs_" + username, user.getOrgsIds(), 3600);
178
+ }
179
+ }
180
+ return userRoles == null ? 0 : userRoles.intValue();
181
+ }
182
+
183
+ // @Override
184
+ public ServerResponse preProcess(HttpRequest request, ResourceMethodInvoker method) throws Failure, WebApplicationException {
185
+ return null;
186
+ }
187
+
188
+ @Override
189
+ public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
190
+ context.proceed();
191
+ EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
192
+ try {
193
+ if (em != null && em.getTransaction().isActive()) {
194
+ if (servletResponse.getStatus() == Status.OK.getStatusCode()) {
195
+ em.getTransaction().commit();
196
+ LOG.debug("COMMIT");
197
+ } else {
198
+ // This code is never executed if there is an error the
199
+ // filter chain is broken
200
+ em.getTransaction().rollback();
201
+ LOG.debug("ROLLBACK");
202
+ }
203
+ }
204
+ } finally {
205
+ if (em.isOpen()) {
206
+ LOG.debug("CLOSING EM: {}, trans: {}", em, em.isJoinedToTransaction());
207
+ try {
208
+ em.close();
209
+ } catch (Exception ex) {
210
+ ex.printStackTrace();
211
+ LOG.error("Error closing EM: {}, {}", em, ex);
212
+ }
213
+ }
214
+ }
215
+ }
192216
193217 }
securis/src/main/java/net/curisit/securis/security/BasicSecurityContext.java
....@@ -11,83 +11,101 @@
1111
1212 public class BasicSecurityContext implements SecurityContext {
1313
14
- final public static String ROL_ADVANCE = "advance";
15
- final public static String ROL_ADMIN = "admin";
14
+ final public static String ROL_ADVANCE = "advance";
15
+ final public static String ROL_ADMIN = "admin";
16
+ final public static String ROL_BASIC = "basic";
1617
17
- final static Map<String, Integer> ROLES = Utils.<String, Integer> createMap(ROL_ADVANCE, User.Rol.ADVANCE, ROL_ADMIN, User.Rol.ADMIN);
18
+ final static Map<String, Integer> ROLES = Utils.<String, Integer> createMap(ROL_BASIC, User.Rol.BASIC, ROL_ADVANCE, User.Rol.ADVANCE, ROL_ADMIN, User.Rol.ADMIN);
1819
19
- Principal user = null;
20
- int roles = 0;
21
- boolean secure = false;
22
- Set<Integer> organizationsIds = null;
23
- double ran = 0;
20
+ Principal user = null;
21
+ int roles = 0;
22
+ boolean secure = false;
23
+ Set<Integer> organizationsIds = null;
24
+ Set<Integer> applicationsIds = null;
25
+ double ran = 0;
2426
25
- public BasicSecurityContext(String username, int roles, boolean secure) {
26
- user = new UserPrincipal(username);
27
- this.roles = roles;
28
- this.secure = secure;
29
- ran = Math.random();
30
- }
27
+ public BasicSecurityContext(String username, int roles, boolean secure) {
28
+ user = new UserPrincipal(username);
29
+ this.roles = roles;
30
+ this.secure = secure;
31
+ ran = Math.random();
32
+ }
3133
32
- @Override
33
- public Principal getUserPrincipal() {
34
- return user;
35
- }
34
+ @Override
35
+ public Principal getUserPrincipal() {
36
+ return user;
37
+ }
3638
37
- @Override
38
- public boolean isUserInRole(String role) {
39
- Integer introle = ROLES.get(role);
40
- return introle != null && (introle & roles) != 0;
41
- }
39
+ @Override
40
+ public boolean isUserInRole(String role) {
41
+ Integer introle = ROLES.get(role);
42
+ return introle != null && (introle & roles) != 0;
43
+ }
4244
43
- @Override
44
- public boolean isSecure() {
45
- return secure;
46
- }
45
+ @Override
46
+ public boolean isSecure() {
47
+ return secure;
48
+ }
4749
48
- @Override
49
- public String getAuthenticationScheme() {
50
- return null;
51
- }
50
+ @Override
51
+ public String getAuthenticationScheme() {
52
+ return null;
53
+ }
5254
53
- @Override
54
- public String toString() {
55
+ @Override
56
+ public String toString() {
5557
56
- return String.format("SecurityContextWrapper(%f) %s", ran, user);
57
- }
58
+ return String.format("SecurityContextWrapper(%f) %s", ran, user);
59
+ }
5860
59
- public void setOrganizationsIds(Set<Integer> orgs) {
60
- this.organizationsIds = orgs;
61
- }
61
+ public void setOrganizationsIds(Set<Integer> orgs) {
62
+ this.organizationsIds = orgs;
63
+ }
6264
63
- public Set<Integer> getOrganizationsIds() {
64
- return this.organizationsIds;
65
- }
65
+ public Set<Integer> getOrganizationsIds() {
66
+ return this.organizationsIds;
67
+ }
6668
67
- private class UserPrincipal implements Principal {
69
+ public Set<Integer> getApplicationsIds() {
70
+ return applicationsIds;
71
+ }
6872
69
- final String name;
73
+ public void setApplicationsIds(Set<Integer> applicationsIds) {
74
+ this.applicationsIds = applicationsIds;
75
+ }
7076
71
- public UserPrincipal(String name) {
72
- this.name = name;
73
- }
77
+ private class UserPrincipal implements Principal {
7478
75
- @Override
76
- public String getName() {
77
- return this.name;
78
- }
79
+ final String name;
7980
80
- @Override
81
- public String toString() {
82
- return String.format("[%s]", name);
83
- }
81
+ public UserPrincipal(String name) {
82
+ this.name = name;
83
+ }
8484
85
- }
85
+ @Override
86
+ public String getName() {
87
+ return this.name;
88
+ }
8689
87
- public boolean isOrgAccesible(Integer orgid) {
88
- if (organizationsIds == null || orgid == null) {
89
- return false;
90
- }
91
- return organizationsIds.contains(orgid);
92
- }
90
+ @Override
91
+ public String toString() {
92
+ return String.format("[%s]", name);
93
+ }
94
+
95
+ }
96
+
97
+ public boolean isOrgAccesible(Integer orgid) {
98
+ if (organizationsIds == null || orgid == null) {
99
+ return false;
100
+ }
101
+ return organizationsIds.contains(orgid);
102
+ }
103
+
104
+ public boolean isAppAccesible(Integer appid) {
105
+ if (applicationsIds == null || appid == null) {
106
+ return false;
107
+ }
108
+ return applicationsIds.contains(appid);
109
+ }
110
+
93111 }
securis/src/main/java/net/curisit/securis/services/PackResource.java
....@@ -90,11 +90,14 @@
9090 LOG.info("Getting all packs for user: " + bsc.getUserPrincipal());
9191 q = em.createNamedQuery("list-packs", Pack.class);
9292 } else {
93
- if (bsc.getOrganizationsIds() == null) {
93
+ if (bsc.getOrganizationsIds() == null || bsc.getOrganizationsIds().isEmpty() || //
94
+ bsc.getApplicationsIds() == null || bsc.getApplicationsIds().isEmpty()) {
9495 return Response.ok().build();
9596 }
96
- q = em.createNamedQuery("list-packs-by-orgs", Pack.class);
97
- q.setParameter("list_ids", bsc.getOrganizationsIds());
97
+ q = em.createNamedQuery("list-packs-by-orgs-apps", Pack.class);
98
+ q.setParameter("list_ids_org", bsc.getOrganizationsIds());
99
+ q.setParameter("list_ids_app", bsc.getApplicationsIds());
100
+ LOG.info("Getting packs from orgs: {} and apps: {}", bsc.getOrganizationsIds(), bsc.getApplicationsIds());
98101 }
99102
100103 List<Pack> list = q.getResultList();
securis/src/main/java/net/curisit/securis/services/UserResource.java
....@@ -34,6 +34,7 @@
3434 import net.curisit.integrity.commons.Utils;
3535 import net.curisit.securis.DefaultExceptionHandler;
3636 import net.curisit.securis.SeCurisException;
37
+import net.curisit.securis.db.Application;
3738 import net.curisit.securis.db.Organization;
3839 import net.curisit.securis.db.User;
3940 import net.curisit.securis.ioc.EnsureTransaction;
....@@ -131,7 +132,12 @@
131132 }
132133
133134 try {
134
- this.setUserOrg(user, user.getOrgsIds(), em);
135
+ this.setUserOrgs(user, user.getOrgsIds(), em);
136
+ } catch (SeCurisException e) {
137
+ return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
138
+ }
139
+ try {
140
+ this.setUserApps(user, user.getAppsIds(), em);
135141 } catch (SeCurisException e) {
136142 return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
137143 }
....@@ -149,7 +155,7 @@
149155 return Response.ok(user).build();
150156 }
151157
152
- private void setUserOrg(User user, Set<Integer> orgsIds, EntityManager em) throws SeCurisException {
158
+ private void setUserOrgs(User user, Set<Integer> orgsIds, EntityManager em) throws SeCurisException {
153159 Set<Organization> orgs = null;
154160 if (orgsIds != null && !orgsIds.isEmpty()) {
155161 orgs = new HashSet<>();
....@@ -165,6 +171,23 @@
165171
166172 user.setOrganizations(orgs);
167173
174
+ }
175
+
176
+ private void setUserApps(User user, Set<Integer> appsIds, EntityManager em) throws SeCurisException {
177
+ Set<Application> apps = null;
178
+ if (appsIds != null && !appsIds.isEmpty()) {
179
+ apps = new HashSet<>();
180
+ for (Integer appId : appsIds) {
181
+ Application o = em.find(Application.class, appId);
182
+ if (o == null) {
183
+ LOG.error("User application with id {} not found in DB", appId);
184
+ throw new SeCurisException("User's application not found with ID: " + appId);
185
+ }
186
+ apps.add(o);
187
+ }
188
+ }
189
+
190
+ user.setApplications(apps);
168191 }
169192
170193 @PUT
....@@ -185,7 +208,12 @@
185208 }
186209
187210 try {
188
- this.setUserOrg(currentUser, user.getOrgsIds(), em);
211
+ this.setUserOrgs(currentUser, user.getOrgsIds(), em);
212
+ } catch (SeCurisException e) {
213
+ return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
214
+ }
215
+ try {
216
+ this.setUserApps(currentUser, user.getAppsIds(), em);
189217 } catch (SeCurisException e) {
190218 return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
191219 }
....@@ -199,8 +227,6 @@
199227 } else {
200228 // Password has not been modified
201229 // return
202
- // Response.status(DefaultExceptionHandler.DEFAULT_APP_ERROR_STATUS_CODE).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER,
203
- // "User password is mandatory").build();
204230 }
205231
206232 currentUser.setLastLogin(user.getLastLogin());
securis/src/main/resources/db/schema.sql
....@@ -73,6 +73,13 @@
7373 username VARCHAR(45) NOT NULL,
7474 organization_id INT NOT NULL,
7575 PRIMARY KEY (username, organization_id));
76
+
77
+drop table IF EXISTS user_application;
78
+CREATE TABLE IF NOT EXISTS user_application (
79
+ username VARCHAR(45) NOT NULL,
80
+ application_id INT NOT NULL,
81
+ PRIMARY KEY (username, application_id));
82
+
7683
7784 drop table IF EXISTS pack;
7885 CREATE TABLE IF NOT EXISTS pack (
securis/src/main/webapp/index.html
....@@ -28,6 +28,7 @@
2828
2929 <!-- Load the Covalent/Material prebuilt theme -->
3030 <link href="node_modules/@covalent/core/theming/prebuilt/blue-orange.css" rel="stylesheet">
31
+
3132 <link href="node_modules/ng2-toastr/bundles/ng2-toastr.min.css" rel="stylesheet" />
3233 <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
3334 </head>
securis/src/main/webapp/package.json
....@@ -1,7 +1,7 @@
11 {
22 "name": "securis",
33 "version": "2.0.0",
4
- "description": "QuickStart package.json from the documentation, supplemented with testing support",
4
+ "description": "SeCuris front-end application based on Angular4 and Materail design",
55 "scripts": {
66 "build": "tsc -p src/",
77 "build:watch": "tsc -p src/ -w",
....@@ -24,18 +24,19 @@
2424 "author": "",
2525 "license": "MIT",
2626 "dependencies": {
27
- "@angular/common": "^2.4.9",
28
- "@angular/compiler": "~2.4.9",
29
- "@angular/core": "~2.4.9",
30
- "@angular/forms": "~2.4.9",
31
- "@angular/http": "~2.4.9",
32
- "@angular/material": "^2.0.0-beta.2",
33
- "@angular/platform-browser": "~2.4.9",
34
- "@angular/platform-browser-dynamic": "~2.4.9",
35
- "@angular/router": "^3.4.9",
36
- "@covalent/core": "^1.0.0-beta.2",
37
- "@covalent/dynamic-forms": "^1.0.0-beta.2",
38
- "@covalent/http": "^1.0.0-beta.2",
27
+ "@angular/common": "^4.0.1",
28
+ "@angular/compiler": "~4.0.1",
29
+ "@angular/core": "~4.0.1",
30
+ "@angular/forms": "~4.0.1",
31
+ "@angular/http": "~4.0.1",
32
+ "@angular/animations": "~4.0.1",
33
+ "@angular/material": "^2.0.0-beta.3",
34
+ "@angular/platform-browser": "~4.0.1",
35
+ "@angular/platform-browser-dynamic": "~4.0.1",
36
+ "@angular/router": "^4.0.1",
37
+ "@covalent/core": "^1.0.0-beta.3",
38
+ "@covalent/dynamic-forms": "^1.0.0-beta.3",
39
+ "@covalent/http": "^1.0.0-beta.3",
3940 "angular-2-local-storage": "^1.0.1",
4041 "angular-in-memory-web-api": "~0.2.4",
4142 "core-js": "^2.4.1",
securis/src/main/webapp/sql_update.sql
....@@ -1,2 +1,10 @@
11 alter table pack add column frozen BOOLEAN NOT NULL default false;
2
-alter table license add column metadata_obsolete BOOLEAN NOT NULL default false;
2
+alter table license add column metadata_obsolete BOOLEAN NOT NULL default false;
3
+
4
+drop table IF EXISTS user_application;
5
+CREATE TABLE IF NOT EXISTS user_application (
6
+ username VARCHAR(45) NOT NULL,
7
+ application_id INT NOT NULL,
8
+ PRIMARY KEY (username, application_id));
9
+
10
+
securis/src/main/webapp/src/app/app.module.ts
....@@ -1,5 +1,6 @@
11 import { NgModule } from '@angular/core';
22 import { BrowserModule } from '@angular/platform-browser';
3
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
34 import { CommonModule } from '@angular/common';
45 import { FormsModule } from '@angular/forms';
56 import {MaterialModule} from '@angular/material';
....@@ -54,6 +55,7 @@
5455 storageType: 'localStorage'
5556 }),
5657 BrowserModule,
58
+ BrowserAnimationsModule,
5759 FormsModule,
5860 MaterialModule,
5961 CovalentCoreModule.forRoot(),
securis/src/main/webapp/src/app/forms/user.form.component.ts
....@@ -29,12 +29,16 @@
2929 })
3030 export class UserFormComponent extends FormBase {
3131 allOrganizations: IComboOption[];
32
+ allApplications: IComboOption[];
3233 orgNames: string[] = [];
33
- allRoles: any[] = [{"id":1, "code": "advance", "label":"Advance"}, {"id":2, "code": "admin","label":"Admin"}];
34
+ appNames: string[] = [];
35
+ allRoles: any[] = [{"id":4, "code": "basic","label":"Basic"}, {"id":1, "code": "advance", "label":"Advance"}, {"id":2, "code": "admin","label":"Admin"}];
3436 user_orgs: string[] = [];
37
+ user_apps: string[] = [];
3538 user_roles: any = {};
3639 constructor(private http: Http,
3740 private users: UsersService,
41
+ private applications: ApplicationsService,
3842 private organizations: OrganizationsService,
3943 router: Router,
4044 toaster: ToastsManager,
....@@ -50,6 +54,10 @@
5054 this.user_orgs.forEach(orgName => {
5155 var selectedOrg = this.allOrganizations.find(org => org.label === orgName);
5256 this.data.organizations_ids.push(selectedOrg.id);
57
+ });
58
+ this.user_apps.forEach(appName => {
59
+ var selectedApp = this.allApplications.find(app => app.label === appName);
60
+ this.data.applications_ids.push(selectedApp.id);
5361 });
5462 this.user_roles.advance && this.data.roles.push(1);
5563 this.user_roles.admin && this.data.roles.push(2);
....@@ -71,6 +79,16 @@
7179 },
7280 err => console.error('Error loading organizations')
7381 );
82
+ this.applications.get()
83
+ .map(list => list.map((app : any) => <IComboOption>{id: app.id, label: app.name}))
84
+ .subscribe(
85
+ data => {
86
+ this.allApplications = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label));
87
+ this.appNames = this.allApplications.map(org => org.label);
88
+ this._loadApps();
89
+ },
90
+ err => console.error('Error loading organizations')
91
+ );
7492 }
7593
7694 goBack(): void {
....@@ -84,6 +102,14 @@
84102 });
85103 }
86104 }
105
+ _loadApps() {
106
+ if (this.data && this.data.applications_ids && this.allApplications && this.allApplications.length > 0) {
107
+ this.data.applications_ids.forEach((appId : number) => {
108
+ var selectedApp = this.allApplications.find(app => app.id === appId);
109
+ this.user_apps.push(selectedApp.label);
110
+ });
111
+ }
112
+ }
87113 init() : void {
88114 this.loadCombos();
89115 this.user_orgs = [];
....@@ -92,6 +118,7 @@
92118 super.reset();
93119 super.prepareInitialData('username', {
94120 organizations_ids: [],
121
+ applications_ids: [],
95122 roles: []
96123 }, (data) => {
97124 this._loadOrgs();
securis/src/main/webapp/src/app/forms/user.form.html
....@@ -7,18 +7,6 @@
77 <span flex></span>
88 <button md-icon-button (click)="save()"><md-icon>save</md-icon></button>
99 </md-toolbar>
10
- <!--
11
- username: 'rym',
12
- roles: [ 1 ],
13
- lastLogin: 1488885433000,
14
- modificationTimestamp: 1479898458000,
15
- email: 'rbouchair@curistec.com',
16
- first_name: 'Rym',
17
- last_name: 'Bouchair',
18
- creation_timestamp: 1479898458000,
19
- organizations_ids: [ 1, 2, 5, 6, 7, 8 ]
20
-}
21
- -->
2210 <div class="margin" layout-align-gt-xs="center start" layout-fill="" layout-gt-xs="row">
2311 <md-card flex="70">
2412 <md-card-title>
....@@ -79,7 +67,7 @@
7967 <error-checker [fieldName]="$L.get('field.email')" [formField]="form.controls.email"></error-checker>
8068 </div>
8169 </div>
82
- <div layout="row" layout-fill layout-padding >
70
+ <div layout="row" layout-fill layout-padding >
8371 <td-chips flex [mdTooltip]="$L.get('Organizations that user can access')" [placeholder]="$L.get('Select organizations')"
8472 [items]="orgNames" [(ngModel)]="user_orgs" name="user_orgs" requireMatch>
8573 </td-chips>
....@@ -92,6 +80,11 @@
9280 </md-checkbox>
9381 </div>
9482 </div>
83
+ <div layout="row" layout-fill layout-padding >
84
+ <td-chips flex [mdTooltip]="$L.get('Applications that user can access')" [placeholder]="$L.get('Select applications')"
85
+ [items]="appNames" [(ngModel)]="user_apps" name="user_apps" requireMatch>
86
+ </td-chips>
87
+ </div>
9588 <div layout="row" layout-fill layout-padding *ngIf="!isNew">
9689 <field-readonly [value]="data.lastLogin || '' | timeAgo" label="field.lastLogin" flex></field-readonly>
9790 <field-readonly [value]="data.creation_timestamp | date: 'medium'" label="field.creation_timestamp" flex></field-readonly>
securis/src/main/webapp/src/app/listing/user.list.component.html
....@@ -6,7 +6,7 @@
66 <span class="push-left-sm" *ngIf="filteredItems < data.length">
77 <span class="md-body-1">{{filteredItems}} of {{data.length}} applications filtered</span>
88 </span>
9
- <td-search-box #searchBox class="push-right-sm" placeholder="Search here" (searchDebounce)="search($event)" flex>
9
+ <td-search-box #searchBox class="push-right-sm" [alwaysVisible]="false" [placeholder]="$L.get('Search here')" (searchDebounce)="search($event)" flex>
1010 </td-search-box>
1111 <button md-mini-fab color="accent" (click)="create()" [mdTooltip]="$L.get('Create a new application')">
1212 <md-icon>add</md-icon>
securis/src/main/webapp/systemjs.config.js
....@@ -4,7 +4,7 @@
44 */
55 (function (global) {
66
7
- var ANGULAR_LIBS = ['core', 'http', 'common', 'compiler', 'material', 'flex-layout', //
7
+ var ANGULAR_LIBS = ['core', 'http', 'common', 'compiler', 'material', 'flex-layout', 'animations', //
88 'router', 'forms', 'platform-browser', 'platform-browser-dynamic', 'common'];
99 var COVALENT_LIBS = ['core', 'http', 'dynamic-forms'];
1010 var mapping = {
....@@ -12,6 +12,8 @@
1212 main: 'src/main.js',
1313 'app': 'src/app',
1414 'environments': 'src/environments',
15
+ '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
16
+ '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
1517
1618 // other libraries
1719 'rxjs': 'npm:rxjs',
....@@ -22,7 +24,7 @@
2224 }
2325
2426 ANGULAR_LIBS.forEach(function (libName) {
25
- mapping['@angular/' + libName] = 'npm:@angular/' + libName + '/bundles/' + libName + '.umd.js';
27
+ mapping['@angular/' + libName] = 'npm:@angular/' + libName + '/bundles/' + libName + '.umd.js';
2628 });
2729
2830 COVALENT_LIBS.forEach(function (libName) {