1 | /* Copyright 2009-2010 the original author or authors. |
---|
2 | * |
---|
3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
---|
4 | * you may not use this file except in compliance with the License. |
---|
5 | * You may obtain a copy of the License at |
---|
6 | * |
---|
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
---|
8 | * |
---|
9 | * Unless required by applicable law or agreed to in writing, software |
---|
10 | * distributed under the License is distributed on an "AS IS" BASIS, |
---|
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
---|
12 | * See the License for the specific language governing permissions and |
---|
13 | * limitations under the License. |
---|
14 | */ |
---|
15 | package dbnp.authentication |
---|
16 | |
---|
17 | import grails.converters.JSON |
---|
18 | import grails.plugins.springsecurity.Secured |
---|
19 | import org.springframework.dao.DataIntegrityViolationException |
---|
20 | |
---|
21 | /** |
---|
22 | * @author <a href='mailto:burt@burtbeckwith.com'>Burt Beckwith</a> |
---|
23 | */ |
---|
24 | @Secured(['ROLE_ADMIN']) |
---|
25 | class UserController { |
---|
26 | |
---|
27 | def userCache |
---|
28 | def springSecurityService |
---|
29 | |
---|
30 | static defaultAction = 'search' |
---|
31 | |
---|
32 | def create = { |
---|
33 | def user = new SecUser(params) |
---|
34 | [user: user, authorityList: sortedRoles()] |
---|
35 | } |
---|
36 | |
---|
37 | def save = { |
---|
38 | def user = new SecUser(params) |
---|
39 | if (params.password) { |
---|
40 | user.password = springSecurityService.encodePassword(params.password, params.username) |
---|
41 | } |
---|
42 | if (!user.save(flush: true)) { |
---|
43 | // Reset encoded password |
---|
44 | user.password = params.password |
---|
45 | render view: 'create', model: [user: user, authorityList: sortedRoles()] |
---|
46 | return |
---|
47 | } |
---|
48 | |
---|
49 | addRoles(user) |
---|
50 | flash.message = "${message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), user.id])}" |
---|
51 | redirect action: edit, id: user.id |
---|
52 | } |
---|
53 | |
---|
54 | def edit = { |
---|
55 | def user = findById() |
---|
56 | |
---|
57 | if (!user) return |
---|
58 | |
---|
59 | return buildUserModel(user) |
---|
60 | } |
---|
61 | |
---|
62 | def update = { |
---|
63 | def user = findById() |
---|
64 | if (!user) return |
---|
65 | |
---|
66 | if (!versionCheck('user.label', 'User', user, [user: user])) { |
---|
67 | return |
---|
68 | } |
---|
69 | |
---|
70 | def oldPassword = user.password |
---|
71 | user.properties = params |
---|
72 | if (params.password && !params.password.equals(oldPassword)) { |
---|
73 | user.password = springSecurityService.encodePassword(params.password, params.username) |
---|
74 | } |
---|
75 | |
---|
76 | if (!user.save()) { |
---|
77 | render view: 'edit', model: buildUserModel(user) |
---|
78 | return |
---|
79 | } |
---|
80 | |
---|
81 | SecUserSecRole.removeAll user |
---|
82 | addRoles user |
---|
83 | |
---|
84 | userCache.removeUserFromCache user.username |
---|
85 | |
---|
86 | flash.message = "${message(code: 'default.updated.message', args: [message(code: 'user.label', default: 'User'), user.id])}" |
---|
87 | redirect action: edit, id: user.id |
---|
88 | } |
---|
89 | |
---|
90 | def delete = { |
---|
91 | def user = findById() |
---|
92 | if (!user) return |
---|
93 | |
---|
94 | try { |
---|
95 | SecUserSecRole.removeAll user |
---|
96 | user.delete flush: true |
---|
97 | |
---|
98 | userCache.removeUserFromCache user.username |
---|
99 | |
---|
100 | flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'user.label', default: 'User'), params.id])}" |
---|
101 | redirect action: search |
---|
102 | } |
---|
103 | catch (DataIntegrityViolationException e) { |
---|
104 | flash.userError = "${message(code: 'default.not.deleted.message', args: [message(code: 'user.label', default: 'User'), params.id])}" |
---|
105 | redirect action: edit, id: params.id |
---|
106 | } |
---|
107 | } |
---|
108 | |
---|
109 | def search = { |
---|
110 | [enabled: 0, accountExpired: 0, accountLocked: 0, passwordExpired: 0] |
---|
111 | } |
---|
112 | |
---|
113 | def userSearch = { |
---|
114 | |
---|
115 | boolean useOffset = params.containsKey('offset') |
---|
116 | params.max = params.max ?: 10 |
---|
117 | params.offset = params.offset ?: 0 |
---|
118 | |
---|
119 | def hql = new StringBuilder('FROM SecUser u WHERE 1=1 ') |
---|
120 | def queryParams = [:] |
---|
121 | |
---|
122 | for (name in ['username']) { |
---|
123 | if (params[name]) { |
---|
124 | hql.append " AND LOWER(u.$name) LIKE :$name" |
---|
125 | queryParams[name] = params[name].toLowerCase() + '%' |
---|
126 | } |
---|
127 | } |
---|
128 | |
---|
129 | for (name in ['enabled', 'accountExpired', 'accountLocked', 'passwordExpired']) { |
---|
130 | int value = params.int(name) |
---|
131 | if (value) { |
---|
132 | hql.append " AND u.$name=:$name" |
---|
133 | queryParams[name] = value == 1 |
---|
134 | } |
---|
135 | } |
---|
136 | |
---|
137 | int totalCount = SecUser.executeQuery("SELECT COUNT(DISTINCT u) $hql", queryParams)[0] |
---|
138 | |
---|
139 | int max = params.int('max') |
---|
140 | int offset = params.int('offset') |
---|
141 | |
---|
142 | String orderBy = '' |
---|
143 | if (params.sort) { |
---|
144 | orderBy = " ORDER BY u.$params.sort ${params.order ?: 'ASC'}" |
---|
145 | } |
---|
146 | |
---|
147 | def results = SecUser.executeQuery( |
---|
148 | "SELECT DISTINCT u $hql $orderBy", |
---|
149 | queryParams, [max: max, offset: offset]) |
---|
150 | def model = [results: results, totalCount: totalCount, searched: true] |
---|
151 | |
---|
152 | // add query params to model for paging |
---|
153 | for (name in ['username', 'enabled', 'accountExpired', 'accountLocked', |
---|
154 | 'passwordExpired', 'sort', 'order']) { |
---|
155 | model[name] = params[name] |
---|
156 | } |
---|
157 | |
---|
158 | render view: 'search', model: model |
---|
159 | } |
---|
160 | |
---|
161 | /** |
---|
162 | * Ajax call used by autocomplete textfield. |
---|
163 | */ |
---|
164 | def ajaxUserSearch = { |
---|
165 | |
---|
166 | def jsonData = [] |
---|
167 | |
---|
168 | if (params.term?.length() > 2) { |
---|
169 | String username = params.term |
---|
170 | |
---|
171 | setIfMissing 'max', 10, 100 |
---|
172 | |
---|
173 | def results = SecUser.executeQuery( |
---|
174 | "SELECT DISTINCT u.username " + |
---|
175 | "FROM SecUser u " + |
---|
176 | "WHERE LOWER(u.username) LIKE :name " + |
---|
177 | "ORDER BY u.username", |
---|
178 | [name: "${username.toLowerCase()}%"], |
---|
179 | [max: params.max]) |
---|
180 | |
---|
181 | for (result in results) { |
---|
182 | jsonData << [value: result] |
---|
183 | } |
---|
184 | } |
---|
185 | |
---|
186 | render text: jsonData as JSON, contentType: 'text/plain' |
---|
187 | } |
---|
188 | |
---|
189 | protected void addRoles(user) { |
---|
190 | for (String key in params.keySet()) { |
---|
191 | if (key.contains('ROLE') && 'on' == params.get(key)) { |
---|
192 | SecUserSecRole.create user, SecRole.findByAuthority(key), true |
---|
193 | } |
---|
194 | } |
---|
195 | } |
---|
196 | |
---|
197 | protected Map buildUserModel(user) { |
---|
198 | |
---|
199 | List roles = sortedRoles() |
---|
200 | Set userRoleNames = user.authorities*.authority |
---|
201 | def granted = [:] |
---|
202 | def notGranted = [:] |
---|
203 | for (role in roles) { |
---|
204 | if (userRoleNames.contains(role.authority)) { |
---|
205 | granted[(role)] = userRoleNames.contains(role.authority) |
---|
206 | } |
---|
207 | else { |
---|
208 | notGranted[(role)] = userRoleNames.contains(role.authority) |
---|
209 | } |
---|
210 | } |
---|
211 | |
---|
212 | return [user: user, roleMap: granted + notGranted] |
---|
213 | } |
---|
214 | |
---|
215 | protected findById() { |
---|
216 | if(!params.id) { |
---|
217 | flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), params.id])}" |
---|
218 | redirect action: search |
---|
219 | } |
---|
220 | |
---|
221 | def user = SecUser.get(params.id) |
---|
222 | |
---|
223 | if (!user) { |
---|
224 | flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), params.id])}" |
---|
225 | redirect action: search |
---|
226 | } |
---|
227 | |
---|
228 | user |
---|
229 | } |
---|
230 | |
---|
231 | protected List sortedRoles() { |
---|
232 | SecRole.list().sort { it.authority } |
---|
233 | } |
---|
234 | |
---|
235 | protected boolean versionCheck(String messageCode, String messageCodeDefault, instance, model) { |
---|
236 | if (params.version) { |
---|
237 | def version = params.version.toLong() |
---|
238 | if (instance.version > version) { |
---|
239 | instance.errors.rejectValue('version', 'default.optimistic.locking.failure', |
---|
240 | [message(code: messageCode, default: messageCodeDefault)] as Object[], |
---|
241 | "Another user has updated this instance while you were editing") |
---|
242 | render view: 'edit', model: model |
---|
243 | return false |
---|
244 | } |
---|
245 | } |
---|
246 | true |
---|
247 | } |
---|
248 | } |
---|