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