You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JettyLoginService.java 13KB

пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit;
  17. import java.io.File;
  18. import java.io.FileReader;
  19. import java.io.FileWriter;
  20. import java.io.IOException;
  21. import java.security.Principal;
  22. import java.text.MessageFormat;
  23. import java.util.ArrayList;
  24. import java.util.HashSet;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Properties;
  28. import java.util.Set;
  29. import javax.security.auth.Subject;
  30. import org.eclipse.jetty.http.security.Credential;
  31. import org.eclipse.jetty.security.IdentityService;
  32. import org.eclipse.jetty.security.MappedLoginService;
  33. import org.eclipse.jetty.server.UserIdentity;
  34. import org.eclipse.jetty.util.log.Log;
  35. import org.slf4j.Logger;
  36. import org.slf4j.LoggerFactory;
  37. import com.gitblit.wicket.models.UserModel;
  38. public class JettyLoginService extends MappedLoginService implements ILoginService {
  39. private final Logger logger = LoggerFactory.getLogger(JettyLoginService.class);
  40. private final File realmFile;
  41. public JettyLoginService(File realmFile) {
  42. super();
  43. setName(Constants.NAME);
  44. this.realmFile = realmFile;
  45. }
  46. @Override
  47. public UserModel authenticate(String username, char[] password) {
  48. UserIdentity identity = login(username, new String(password));
  49. if (identity == null || identity.equals(UserIdentity.UNAUTHENTICATED_IDENTITY)) {
  50. return null;
  51. }
  52. UserModel user = new UserModel(username);
  53. user.canAdmin(identity.isUserInRole(Constants.ADMIN_ROLE, null));
  54. // Add repositories
  55. for (Principal principal : identity.getSubject().getPrincipals()) {
  56. if (principal instanceof RolePrincipal) {
  57. RolePrincipal role = (RolePrincipal) principal;
  58. String roleName = role.getName();
  59. if (roleName.charAt(0) != '#') {
  60. user.addRepository(roleName);
  61. }
  62. }
  63. }
  64. return user;
  65. }
  66. @Override
  67. public UserModel getUserModel(String username) {
  68. UserIdentity identity = _users.get(username);
  69. if (identity == null) {
  70. return null;
  71. }
  72. UserModel model = new UserModel(username);
  73. Subject subject = identity.getSubject();
  74. for (Principal principal : subject.getPrincipals()) {
  75. if (principal instanceof RolePrincipal) {
  76. RolePrincipal role = (RolePrincipal) principal;
  77. String name = role.getName();
  78. switch (name.charAt(0)) {
  79. case '#':
  80. // Permissions
  81. if (name.equalsIgnoreCase(Constants.ADMIN_ROLE)) {
  82. model.canAdmin(true);
  83. }
  84. break;
  85. default:
  86. model.addRepository(name);
  87. }
  88. }
  89. }
  90. // Retrieve the password from the realm file.
  91. // Stupid, I know, but the password is buried within protected inner
  92. // classes in private variables. Too much work to reflectively retrieve.
  93. try {
  94. Properties allUsers = readRealmFile();
  95. String value = allUsers.getProperty(username);
  96. String password = value.split(",")[0];
  97. model.setPassword(password);
  98. } catch (Throwable t) {
  99. logger.error(MessageFormat.format("Failed to read password for user {0}!", username), t);
  100. }
  101. return model;
  102. }
  103. @Override
  104. public boolean updateUserModel(UserModel model) {
  105. return updateUserModel(model.getUsername(), model);
  106. }
  107. @Override
  108. public boolean updateUserModel(String username, UserModel model) {
  109. try {
  110. Properties allUsers = readRealmFile();
  111. ArrayList<String> roles = new ArrayList<String>(model.getRepositories());
  112. // Permissions
  113. if (model.canAdmin()) {
  114. roles.add(Constants.ADMIN_ROLE);
  115. }
  116. StringBuilder sb = new StringBuilder();
  117. sb.append(model.getPassword());
  118. sb.append(',');
  119. for (String role : roles) {
  120. sb.append(role);
  121. sb.append(',');
  122. }
  123. // trim trailing comma
  124. sb.setLength(sb.length() - 1);
  125. allUsers.remove(username);
  126. allUsers.put(model.getUsername(), sb.toString());
  127. writeRealmFile(allUsers);
  128. // Update login service
  129. removeUser(username);
  130. putUser(model.getUsername(), Credential.getCredential(model.getPassword()), roles.toArray(new String[0]));
  131. return true;
  132. } catch (Throwable t) {
  133. logger.error(MessageFormat.format("Failed to update user model {0}!", model.getUsername()), t);
  134. }
  135. return false;
  136. }
  137. @Override
  138. public boolean deleteUserModel(UserModel model) {
  139. return deleteUser(model.getUsername());
  140. }
  141. @Override
  142. public boolean deleteUser(String username) {
  143. try {
  144. // Read realm file
  145. Properties allUsers = readRealmFile();
  146. allUsers.remove(username);
  147. writeRealmFile(allUsers);
  148. // Drop user from map
  149. removeUser(username);
  150. return true;
  151. } catch (Throwable t) {
  152. logger.error(MessageFormat.format("Failed to delete user {0}!", username), t);
  153. }
  154. return false;
  155. }
  156. @Override
  157. public List<String> getAllUsernames() {
  158. List<String> list = new ArrayList<String>();
  159. list.addAll(_users.keySet());
  160. return list;
  161. }
  162. @Override
  163. public List<String> getUsernamesForRole(String role) {
  164. List<String> list = new ArrayList<String>();
  165. try {
  166. Properties allUsers = readRealmFile();
  167. for (String username : allUsers.stringPropertyNames()) {
  168. String value = allUsers.getProperty(username);
  169. String[] values = value.split(",");
  170. // skip first value (password)
  171. for (int i = 1; i < values.length; i++) {
  172. String r = values[i];
  173. if (r.equalsIgnoreCase(role)) {
  174. list.add(username);
  175. break;
  176. }
  177. }
  178. }
  179. } catch (Throwable t) {
  180. logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t);
  181. }
  182. return list;
  183. }
  184. @Override
  185. public boolean setUsernamesForRole(String role, List<String> usernames) {
  186. try {
  187. Set<String> specifiedUsers = new HashSet<String>(usernames);
  188. Set<String> needsAddRole = new HashSet<String>(specifiedUsers);
  189. Set<String> needsRemoveRole = new HashSet<String>();
  190. // identify users which require add and remove role
  191. Properties allUsers = readRealmFile();
  192. for (String username : allUsers.stringPropertyNames()) {
  193. String value = allUsers.getProperty(username);
  194. String[] values = value.split(",");
  195. // skip first value (password)
  196. for (int i = 1; i < values.length; i++) {
  197. String r = values[i];
  198. if (r.equalsIgnoreCase(role)) {
  199. // user has role, check against revised user list
  200. if (specifiedUsers.contains(username)) {
  201. needsAddRole.remove(username);
  202. } else {
  203. // remove role from user
  204. needsRemoveRole.add(username);
  205. }
  206. break;
  207. }
  208. }
  209. }
  210. // add roles to users
  211. for (String user : needsAddRole) {
  212. String userValues = allUsers.getProperty(user);
  213. userValues += ("," + role);
  214. allUsers.put(user, userValues);
  215. String[] values = userValues.split(",");
  216. String password = values[0];
  217. String[] roles = new String[values.length - 1];
  218. System.arraycopy(values, 1, roles, 0, values.length - 1);
  219. putUser(user, Credential.getCredential(password), roles);
  220. }
  221. // remove role from user
  222. for (String user : needsRemoveRole) {
  223. String[] values = allUsers.getProperty(user).split(",");
  224. String password = values[0];
  225. StringBuilder sb = new StringBuilder();
  226. sb.append(password);
  227. sb.append(',');
  228. List<String> revisedRoles = new ArrayList<String>();
  229. // skip first value (password)
  230. for (int i = 1; i < values.length; i++) {
  231. String value = values[i];
  232. if (!value.equalsIgnoreCase(role)) {
  233. revisedRoles.add(value);
  234. sb.append(value);
  235. sb.append(',');
  236. }
  237. }
  238. sb.setLength(sb.length() - 1);
  239. // update properties
  240. allUsers.put(user, sb.toString());
  241. // update memory
  242. putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0]));
  243. }
  244. // persist changes
  245. writeRealmFile(allUsers);
  246. return true;
  247. } catch (Throwable t) {
  248. logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t);
  249. }
  250. return false;
  251. }
  252. @Override
  253. public boolean renameRole(String oldRole, String newRole) {
  254. try {
  255. Properties allUsers = readRealmFile();
  256. Set<String> needsRenameRole = new HashSet<String>();
  257. // identify users which require role rename
  258. for (String username : allUsers.stringPropertyNames()) {
  259. String value = allUsers.getProperty(username);
  260. String[] roles = value.split(",");
  261. // skip first value (password)
  262. for (int i = 1; i < roles.length; i++) {
  263. String r = roles[i];
  264. if (r.equalsIgnoreCase(oldRole)) {
  265. needsRenameRole.remove(username);
  266. break;
  267. }
  268. }
  269. }
  270. // rename role for identified users
  271. for (String user : needsRenameRole) {
  272. String userValues = allUsers.getProperty(user);
  273. String[] values = userValues.split(",");
  274. String password = values[0];
  275. StringBuilder sb = new StringBuilder();
  276. sb.append(password);
  277. sb.append(',');
  278. List<String> revisedRoles = new ArrayList<String>();
  279. revisedRoles.add(newRole);
  280. // skip first value (password)
  281. for (int i = 1; i < values.length; i++) {
  282. String value = values[i];
  283. if (!value.equalsIgnoreCase(oldRole)) {
  284. revisedRoles.add(value);
  285. sb.append(value);
  286. sb.append(',');
  287. }
  288. }
  289. sb.setLength(sb.length() - 1);
  290. // update properties
  291. allUsers.put(user, sb.toString());
  292. // update memory
  293. putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0]));
  294. }
  295. // persist changes
  296. writeRealmFile(allUsers);
  297. return true;
  298. } catch (Throwable t) {
  299. logger.error(MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);
  300. }
  301. return false;
  302. }
  303. @Override
  304. public boolean deleteRole(String role) {
  305. try {
  306. Properties allUsers = readRealmFile();
  307. Set<String> needsDeleteRole = new HashSet<String>();
  308. // identify users which require role rename
  309. for (String username : allUsers.stringPropertyNames()) {
  310. String value = allUsers.getProperty(username);
  311. String[] roles = value.split(",");
  312. // skip first value (password)
  313. for (int i = 1; i < roles.length; i++) {
  314. String r = roles[i];
  315. if (r.equalsIgnoreCase(role)) {
  316. needsDeleteRole.remove(username);
  317. break;
  318. }
  319. }
  320. }
  321. // delete role for identified users
  322. for (String user : needsDeleteRole) {
  323. String userValues = allUsers.getProperty(user);
  324. String[] values = userValues.split(",");
  325. String password = values[0];
  326. StringBuilder sb = new StringBuilder();
  327. sb.append(password);
  328. sb.append(',');
  329. List<String> revisedRoles = new ArrayList<String>();
  330. // skip first value (password)
  331. for (int i = 1; i < values.length; i++) {
  332. String value = values[i];
  333. if (!value.equalsIgnoreCase(role)) {
  334. revisedRoles.add(value);
  335. sb.append(value);
  336. sb.append(',');
  337. }
  338. }
  339. sb.setLength(sb.length() - 1);
  340. // update properties
  341. allUsers.put(user, sb.toString());
  342. // update memory
  343. putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0]));
  344. }
  345. // persist changes
  346. writeRealmFile(allUsers);
  347. return true;
  348. } catch (Throwable t) {
  349. logger.error(MessageFormat.format("Failed to delete role {0}!", role), t);
  350. }
  351. return false;
  352. }
  353. private Properties readRealmFile() throws IOException {
  354. Properties allUsers = new Properties();
  355. FileReader reader = new FileReader(realmFile);
  356. allUsers.load(reader);
  357. reader.close();
  358. return allUsers;
  359. }
  360. private void writeRealmFile(Properties properties) throws IOException {
  361. // Update realm file
  362. File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");
  363. FileWriter writer = new FileWriter(realmFileCopy);
  364. properties.store(writer, "# Git:Blit realm file format: username=password,\\#permission,repository1,repository2...");
  365. writer.close();
  366. if (realmFileCopy.exists() && realmFileCopy.length() > 0) {
  367. realmFile.delete();
  368. realmFileCopy.renameTo(realmFile);
  369. } else {
  370. throw new IOException("Failed to save realmfile!");
  371. }
  372. }
  373. /* ------------------------------------------------------------ */
  374. @Override
  375. public void loadUsers() throws IOException {
  376. if (realmFile == null)
  377. return;
  378. if (Log.isDebugEnabled())
  379. Log.debug("Load " + this + " from " + realmFile);
  380. Properties allUsers = readRealmFile();
  381. // Map Users
  382. for (Map.Entry<Object, Object> entry : allUsers.entrySet()) {
  383. String username = ((String) entry.getKey()).trim();
  384. String credentials = ((String) entry.getValue()).trim();
  385. String roles = null;
  386. int c = credentials.indexOf(',');
  387. if (c > 0) {
  388. roles = credentials.substring(c + 1).trim();
  389. credentials = credentials.substring(0, c).trim();
  390. }
  391. if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0) {
  392. String[] roleArray = IdentityService.NO_ROLES;
  393. if (roles != null && roles.length() > 0) {
  394. roleArray = roles.split(",");
  395. }
  396. putUser(username, Credential.getCredential(credentials), roleArray);
  397. }
  398. }
  399. }
  400. @Override
  401. protected UserIdentity loadUser(String username) {
  402. return null;
  403. }
  404. }