1 | 2 | |
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package org.apache.shiro.samples.aspectj.bank; |
20 | |
|
21 | |
import org.apache.shiro.SecurityUtils; |
22 | |
import org.apache.shiro.authz.annotation.RequiresPermissions; |
23 | |
import org.apache.shiro.samples.aspectj.bank.AccountTransaction.TransactionType; |
24 | |
import org.apache.shiro.subject.Subject; |
25 | |
import org.slf4j.Logger; |
26 | |
import org.slf4j.LoggerFactory; |
27 | |
|
28 | |
import java.util.ArrayList; |
29 | |
import java.util.HashMap; |
30 | |
import java.util.List; |
31 | |
import java.util.Map; |
32 | |
|
33 | |
public class SecureBankService implements BankService { |
34 | |
|
35 | 2 | private static final Logger log = LoggerFactory.getLogger(SecureBankService.class); |
36 | |
private volatile boolean _isRunning; |
37 | |
private final List<Account> _accounts; |
38 | |
private Map<Long, Account> _accountsById; |
39 | |
|
40 | |
|
41 | |
|
42 | |
|
43 | 2 | public SecureBankService() { |
44 | 2 | _accounts = new ArrayList<Account>(); |
45 | 2 | _accountsById = new HashMap<Long, Account>(); |
46 | 2 | } |
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
public void start() throws Exception { |
52 | 2 | _isRunning = true; |
53 | 2 | log.info("Bank service started"); |
54 | 2 | } |
55 | |
|
56 | |
|
57 | |
|
58 | |
|
59 | |
public void dispose() { |
60 | 2 | log.info("Stopping bank service..."); |
61 | 2 | _isRunning = false; |
62 | |
|
63 | 4 | synchronized (_accounts) { |
64 | 2 | _accountsById.clear(); |
65 | 2 | _accounts.clear(); |
66 | |
} |
67 | |
|
68 | 2 | log.info("Bank service stopped"); |
69 | 2 | } |
70 | |
|
71 | |
|
72 | |
|
73 | |
|
74 | |
protected void assertServiceState() { |
75 | 384 | if (!_isRunning) { |
76 | 0 | throw new IllegalStateException("This bank service is not running"); |
77 | |
} |
78 | 384 | } |
79 | |
|
80 | |
public int getAccountCount() { |
81 | 0 | return _accounts.size(); |
82 | |
} |
83 | |
|
84 | |
|
85 | |
|
86 | |
|
87 | |
|
88 | |
@RequiresPermissions("bankAccount:create") |
89 | |
public long createNewAccount(String anOwnerName) { |
90 | 24 | assertServiceState(); |
91 | 24 | log.info("Creating new account for " + anOwnerName); |
92 | |
|
93 | 24 | synchronized (_accounts) { |
94 | 24 | Account account = new Account(anOwnerName); |
95 | 24 | account.setCreatedBy(getCurrentUsername()); |
96 | 24 | _accounts.add(account); |
97 | 24 | _accountsById.put(account.getId(), account); |
98 | |
|
99 | 24 | log.debug("Created new account: " + account); |
100 | 24 | return account.getId(); |
101 | |
} |
102 | |
} |
103 | |
|
104 | |
|
105 | |
|
106 | |
|
107 | |
|
108 | |
public long[] searchAccountIdsByOwner(String anOwnerName) { |
109 | 0 | assertServiceState(); |
110 | 0 | log.info("Searching existing accounts for " + anOwnerName); |
111 | |
|
112 | 0 | ArrayList<Account> matchAccounts = new ArrayList<Account>(); |
113 | 0 | synchronized (_accounts) { |
114 | 0 | for (Account a : _accounts) { |
115 | 0 | if (a.getOwnerName().toLowerCase().contains(anOwnerName.toLowerCase())) { |
116 | 0 | matchAccounts.add(a); |
117 | |
} |
118 | |
} |
119 | |
} |
120 | |
|
121 | 0 | long[] accountIds = new long[matchAccounts.size()]; |
122 | 0 | int index = 0; |
123 | 0 | for (Account a : matchAccounts) { |
124 | 0 | accountIds[index++] = a.getId(); |
125 | |
} |
126 | |
|
127 | 0 | log.debug("Found " + accountIds.length + " account(s) matching the name " + anOwnerName); |
128 | 0 | return accountIds; |
129 | |
} |
130 | |
|
131 | |
|
132 | |
|
133 | |
|
134 | |
|
135 | |
@RequiresPermissions("bankAccount:read") |
136 | |
public String getOwnerOf(long anAccountId) throws AccountNotFoundException { |
137 | 66 | assertServiceState(); |
138 | 66 | log.info("Getting owner of account " + anAccountId); |
139 | |
|
140 | 66 | Account a = safellyRetrieveAccountForId(anAccountId); |
141 | 66 | return a.getOwnerName(); |
142 | |
} |
143 | |
|
144 | |
|
145 | |
|
146 | |
|
147 | |
|
148 | |
@RequiresPermissions("bankAccount:read") |
149 | |
public double getBalanceOf(long anAccountId) throws AccountNotFoundException { |
150 | 94 | assertServiceState(); |
151 | 94 | log.info("Getting balance of account " + anAccountId); |
152 | |
|
153 | 94 | Account a = safellyRetrieveAccountForId(anAccountId); |
154 | 94 | return a.getBalance(); |
155 | |
} |
156 | |
|
157 | |
|
158 | |
|
159 | |
|
160 | |
|
161 | |
@RequiresPermissions("bankAccount:operate") |
162 | |
public double depositInto(long anAccountId, double anAmount) throws AccountNotFoundException, InactiveAccountException { |
163 | 18 | assertServiceState(); |
164 | 18 | log.info("Making deposit of " + anAmount + " into account " + anAccountId); |
165 | |
|
166 | |
try { |
167 | 18 | Account a = safellyRetrieveAccountForId(anAccountId); |
168 | 18 | AccountTransaction tx = AccountTransaction.createDepositTx(anAccountId, anAmount); |
169 | 18 | tx.setCreatedBy(getCurrentUsername()); |
170 | 18 | log.debug("Created a new transaction " + tx); |
171 | |
|
172 | 18 | a.applyTransaction(tx); |
173 | 18 | log.debug("New balance of account " + a.getId() + " after deposit is " + a.getBalance()); |
174 | |
|
175 | 18 | return a.getBalance(); |
176 | |
|
177 | 0 | } catch (NotEnoughFundsException nefe) { |
178 | 0 | throw new IllegalStateException("Should never happen", nefe); |
179 | |
} |
180 | |
} |
181 | |
|
182 | |
|
183 | |
|
184 | |
|
185 | |
|
186 | |
@RequiresPermissions("bankAccount:operate") |
187 | |
public double withdrawFrom(long anAccountId, double anAmount) throws AccountNotFoundException, NotEnoughFundsException, InactiveAccountException { |
188 | 14 | assertServiceState(); |
189 | 14 | log.info("Making withdrawal of " + anAmount + " from account " + anAccountId); |
190 | |
|
191 | 14 | Account a = safellyRetrieveAccountForId(anAccountId); |
192 | 14 | AccountTransaction tx = AccountTransaction.createWithdrawalTx(anAccountId, anAmount); |
193 | 14 | tx.setCreatedBy(getCurrentUsername()); |
194 | 14 | log.debug("Created a new transaction " + tx); |
195 | |
|
196 | 14 | a.applyTransaction(tx); |
197 | 10 | log.debug("New balance of account " + a.getId() + " after withdrawal is " + a.getBalance()); |
198 | |
|
199 | 10 | return a.getBalance(); |
200 | |
} |
201 | |
|
202 | |
|
203 | |
|
204 | |
|
205 | |
|
206 | |
@RequiresPermissions("bankAccount:read") |
207 | |
public TxLog[] getTxHistoryFor(long anAccountId) throws AccountNotFoundException { |
208 | 94 | assertServiceState(); |
209 | 94 | log.info("Getting transactions of account " + anAccountId); |
210 | |
|
211 | 94 | Account a = safellyRetrieveAccountForId(anAccountId); |
212 | |
|
213 | 94 | TxLog[] txs = new TxLog[a.getTransactions().size()]; |
214 | 94 | int index = 0; |
215 | 290 | for (AccountTransaction tx : a.getTransactions()) { |
216 | 102 | log.debug("Retrieved transaction " + tx); |
217 | |
|
218 | 102 | if (TransactionType.DEPOSIT == tx.getType()) { |
219 | 64 | txs[index++] = new TxLog(tx.getCreationDate(), tx.getAmount(), tx.getCreatedBy()); |
220 | 64 | } else { |
221 | 38 | txs[index++] = new TxLog(tx.getCreationDate(), -1.0d * tx.getAmount(), tx.getCreatedBy()); |
222 | |
} |
223 | |
} |
224 | |
|
225 | 94 | return txs; |
226 | |
} |
227 | |
|
228 | |
|
229 | |
|
230 | |
|
231 | |
|
232 | |
@RequiresPermissions("bankAccount:close") |
233 | |
public double closeAccount(long anAccountId) throws AccountNotFoundException, InactiveAccountException { |
234 | 10 | assertServiceState(); |
235 | 8 | log.info("Closing account " + anAccountId); |
236 | |
|
237 | 8 | Account a = safellyRetrieveAccountForId(anAccountId); |
238 | 8 | if (!a.isActive()) { |
239 | 2 | throw new InactiveAccountException("The account " + anAccountId + " is already closed"); |
240 | |
} |
241 | |
|
242 | |
try { |
243 | 6 | AccountTransaction tx = AccountTransaction.createWithdrawalTx(a.getId(), a.getBalance()); |
244 | 6 | tx.setCreatedBy(getCurrentUsername()); |
245 | 6 | log.debug("Created a new transaction " + tx); |
246 | 6 | a.applyTransaction(tx); |
247 | 6 | a.setActive(false); |
248 | |
|
249 | 6 | log.debug("Account " + a.getId() + " is now closed and an amount of " + tx.getAmount() + " is given to the owner"); |
250 | 6 | return tx.getAmount(); |
251 | |
|
252 | 0 | } catch (NotEnoughFundsException nefe) { |
253 | 0 | throw new IllegalStateException("Should never happen", nefe); |
254 | |
} |
255 | |
} |
256 | |
|
257 | |
|
258 | |
|
259 | |
|
260 | |
|
261 | |
@RequiresPermissions("bankAccount:read") |
262 | |
public boolean isAccountActive(long anAccountId) throws AccountNotFoundException { |
263 | 66 | assertServiceState(); |
264 | 66 | log.info("Getting active status of account " + anAccountId); |
265 | |
|
266 | 66 | Account a = safellyRetrieveAccountForId(anAccountId); |
267 | 66 | return a.isActive(); |
268 | |
} |
269 | |
|
270 | |
|
271 | |
|
272 | |
|
273 | |
|
274 | |
|
275 | |
|
276 | |
|
277 | |
|
278 | |
protected Account safellyRetrieveAccountForId(long anAccountId) throws AccountNotFoundException { |
279 | 360 | Account account = null; |
280 | 720 | synchronized (_accounts) { |
281 | 360 | account = _accountsById.get(anAccountId); |
282 | |
} |
283 | |
|
284 | 360 | if (account == null) { |
285 | 0 | throw new AccountNotFoundException("No account found for the id " + anAccountId); |
286 | |
} |
287 | |
|
288 | 360 | log.info("Retrieved account " + account); |
289 | 360 | return account; |
290 | |
} |
291 | |
|
292 | |
|
293 | |
|
294 | |
|
295 | |
|
296 | |
|
297 | |
protected String getCurrentUsername() { |
298 | 62 | Subject subject = SecurityUtils.getSubject(); |
299 | 62 | if (subject == null || subject.getPrincipal() == null || !subject.isAuthenticated()) { |
300 | 0 | throw new IllegalStateException("Unable to retrieve the current authenticated subject"); |
301 | |
} |
302 | 62 | return SecurityUtils.getSubject().getPrincipal().toString(); |
303 | |
} |
304 | |
} |