PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/memedb/src/java/memedb/auth/BasicAuthentication.java

http://caffeine-hx.googlecode.com/
Java | 301 lines | 221 code | 27 blank | 53 comment | 37 complexity | dc8069d66a25d0505d1362d148921aeb MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, BSD-3-Clause, LGPL-2.1
  1. /*
  2. * Copyright 2008 The MemeDB Contributors (see CONTRIBUTORS)
  3. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  4. * use this file except in compliance with the License. You may obtain a copy of
  5. * 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, WITHOUT
  11. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. * License for the specific language governing permissions and limitations under
  13. * the License.
  14. */
  15. package memedb.auth;
  16. import java.util.ArrayList;
  17. import java.util.Collections;
  18. import java.util.List;
  19. import java.util.Random;
  20. import java.util.HashMap;
  21. import memedb.MemeDB;
  22. import memedb.backend.BackendException;
  23. import memedb.document.Document;
  24. import memedb.document.DocumentCreationException;
  25. import memedb.document.JSONDocument;
  26. import memedb.utils.BCrypt;
  27. //import memedb.utils.Lock;
  28. import memedb.utils.Logger;
  29. import org.json.JSONObject;
  30. /**
  31. * <p>
  32. * This authentication backend does two things for checking a username/password. First, it loads an administrator
  33. * (sa) username and password from the properties file. Second, it checks to see if a system document
  34. * "_users/username" exists.
  35. * <p>
  36. * The user document must contain a "password" entry, and _can_ contain a boolean "is_sa" flag.
  37. *
  38. * @author mbreese
  39. *
  40. */
  41. public class BasicAuthentication implements Authentication {
  42. protected Logger log = Logger.get(BasicAuthentication.class);
  43. protected List<Credentials> credentials = Collections.synchronizedList(new ArrayList<Credentials>());
  44. protected static Random random = new java.util.Random();
  45. protected Thread monitor = null;
  46. protected String saUsername;
  47. protected String saPasswordHash;
  48. protected MemeDB memeDB;
  49. protected int timeout;
  50. private boolean allowAnonymous;
  51. public BasicAuthentication () {
  52. }
  53. public void init(MemeDB memeDB) {
  54. this.memeDB = memeDB;
  55. if (!memeDB.getBackend().doesDatabaseExist(MemeDB.USERS_DB)) {
  56. try {
  57. memeDB.addDatabase(MemeDB.USERS_DB);
  58. } catch (Exception e) {
  59. e.printStackTrace();
  60. log.error(e);
  61. throw new RuntimeException(e);
  62. }
  63. }
  64. String u = memeDB.getProperty("sa.username");
  65. String p = memeDB.getProperty("sa.password");
  66. this.saUsername=u;
  67. this.saPasswordHash=BCrypt.hashpw(p, BCrypt.gensalt());
  68. this.allowAnonymous = memeDB.getProperty("auth.anonymous","false").toLowerCase().equals("true");
  69. this.timeout=Integer.parseInt(memeDB.getProperty("auth.timeout.seconds","300"));
  70. JSONDocument userdoc = (JSONDocument) memeDB.getBackend().getDocument(MemeDB.USERS_DB, "anonymous");
  71. if (userdoc==null) {
  72. log.warn("Creating default anonymous user");
  73. try {
  74. JSONObject o = new JSONObject(AnonCredentials.defaultJSON());
  75. this.addUser(new SACredentials("system","system",0), o);
  76. } catch(NotAuthorizedException e) {
  77. throw new RuntimeException("Unable to create user 'anonymous'", e);
  78. }
  79. } else {
  80. AnonCredentials.setDefaultAnonDocument(userdoc);
  81. }
  82. monitor = new Thread() {
  83. boolean stop = false;
  84. @Override
  85. public void run() {
  86. log.debug("Starting authentication cache monitoring thread");
  87. while (!stop) {
  88. List<Credentials> expired = new ArrayList<Credentials>();
  89. for (Credentials cred:credentials) {
  90. if (cred.isExpired()) {
  91. expired.add(cred);
  92. }
  93. }
  94. for (Credentials cred: expired) {
  95. log.debug("Invalidating credentials {} (timeout)", cred.getUsername());
  96. invalidate(cred);
  97. }
  98. try {
  99. Thread.sleep(5000);
  100. } catch (InterruptedException e) {
  101. stop = true;
  102. }
  103. }
  104. log.debug("Stopping authentication cache monitoring thread");
  105. }
  106. @Override
  107. public void interrupt() {
  108. this.stop = true;
  109. super.interrupt();
  110. }
  111. };
  112. monitor.start();
  113. }
  114. public void shutdown() {
  115. monitor.interrupt();
  116. }
  117. public Credentials addCredentials(Credentials cred) {
  118. log.debug("Adding credentials {} to cache", cred.getUsername());
  119. credentials.add(cred);
  120. return cred;
  121. }
  122. /*
  123. public void addUser(Credentials cred, String username, String password, HashMap<String, String> dbPerms, boolean sa) throws NotAuthorizedException {
  124. if (cred.isSA()) {
  125. try {
  126. JSONDocument userdoc = (JSONDocument) Document.newDocument(memeDB.getBackend(),MemeDB.USERS_DB, username, MemeDB.CONTENT_JSON, cred.getUsername());
  127. userdoc.put("username", username);
  128. userdoc.put("is_sa", sa);
  129. userdoc.put("password", BCrypt.hashpw(password, BCrypt.gensalt()));
  130. userdoc.put("db_access", dbPerms);
  131. memeDB.getBackend().saveDocument(userdoc);
  132. } catch (BackendException e) {
  133. log.error("Backend exception adding user: {}",e,username);
  134. } catch (DocumentCreationException e) {
  135. log.error("Backend exception adding user: {}",e,username);
  136. }
  137. } else {
  138. throw new NotAuthorizedException("Only sa users can add new users");
  139. }
  140. }
  141. */
  142. public void addUser(Credentials cred, JSONObject definition) throws NotAuthorizedException
  143. {
  144. if(cred.isSA()) {
  145. try {
  146. JSONDocument userdoc = makeDoc(cred, definition, false);
  147. removeUser(cred, (String)userdoc.get("username"));
  148. memeDB.getBackend().saveDocument(userdoc);
  149. if("anonymous".equals((String)userdoc.get("username"))) {
  150. AnonCredentials.setDefaultAnonDocument(userdoc);
  151. }
  152. } catch(BackendException e) {
  153. log.error("Backend exception adding user : {}", e);
  154. } catch (DocumentCreationException e) {
  155. log.error("Backend exception adding user : {}", e);
  156. }
  157. } else {
  158. throw new NotAuthorizedException("Only sa users can add new users");
  159. }
  160. }
  161. public Credentials authenticate(String username, String password) {
  162. if (password==null) {
  163. password="";
  164. }
  165. log.debug("Attempting authentication: {}", username);
  166. if (username.equals(saUsername) && BCrypt.checkpw(password,saPasswordHash)) {
  167. return addCredentials(new SACredentials(username,generateToken(),timeout));
  168. } else {
  169. if (!allowAnonymous && "anonymous".equals(username)) {
  170. return null;
  171. }
  172. JSONDocument userdoc = (JSONDocument) memeDB.getBackend().getDocument(MemeDB.USERS_DB,username);
  173. if (userdoc!=null) {
  174. String hashedPassword = (String) userdoc.get("password");
  175. if (hashedPassword == null) {
  176. hashedPassword = BCrypt.hashpw("", BCrypt.gensalt());
  177. }
  178. if (BCrypt.checkpw(password,hashedPassword)) {
  179. if("anonymous".equals(username))
  180. return addCredentials(new AnonCredentials(userdoc,generateToken(),timeout));
  181. return addCredentials(new UserCredentials(userdoc,generateToken(),timeout));
  182. } else {
  183. log.debug("Invalid password for user {}",username);
  184. }
  185. } else {
  186. log.debug("No document for user {} found",username);
  187. }
  188. }
  189. return null;
  190. }
  191. public synchronized String generateToken() {
  192. String token = null;
  193. while (token==null || getCredentialsFromToken(token)!=null) {
  194. token = Long.toHexString(random.nextLong())+Long.toHexString(random.nextLong());
  195. }
  196. return token;
  197. }
  198. public List<Credentials> getCredentials() {
  199. return credentials;
  200. }
  201. public Credentials getCredentialsFromToken(String token) {
  202. for (Credentials cred:credentials) {
  203. if (cred.getToken().equals(token)) {
  204. if (!cred.isExpired()) {
  205. cred.resetTimeout();
  206. return cred;
  207. } else {
  208. invalidate(cred);
  209. }
  210. }
  211. }
  212. return null;
  213. }
  214. public void invalidate(Credentials cred) {
  215. if (credentials.contains(cred)) {
  216. credentials.remove(cred);
  217. }
  218. }
  219. public void removeUser(Credentials cred, String username) throws NotAuthorizedException {
  220. if (cred.isSA()) {
  221. try {
  222. memeDB.getBackend().deleteDocument(MemeDB.USERS_DB,username);
  223. } catch (BackendException e) {
  224. //log.error("Backend exception removing user: {}",e,username);
  225. }
  226. } else {
  227. throw new NotAuthorizedException("Only sa users can remove users");
  228. }
  229. }
  230. public void updateUser(Credentials cred, JSONObject definition) throws NotAuthorizedException
  231. {
  232. if(cred.isSA()) {
  233. try {
  234. JSONDocument userdoc = makeDoc(cred, definition, true);
  235. removeUser(cred, (String)userdoc.get("username"));
  236. memeDB.getBackend().saveDocument(userdoc);
  237. if("anonymous".equals((String)userdoc.get("username"))) {
  238. AnonCredentials.setDefaultAnonDocument(userdoc);
  239. }
  240. } catch(BackendException e) {
  241. log.error("Backend exception adding user : {}", e);
  242. } catch (DocumentCreationException e) {
  243. log.error("Backend exception adding user : {}", e);
  244. }
  245. } else {
  246. throw new NotAuthorizedException("Only sa users can add new users");
  247. }
  248. }
  249. /**
  250. * Creates a JSONDocument from the user rights definition.
  251. * @param definition JSONObject with user rights
  252. * @param forUpdate Set to true to not rehash the user's password
  253. * @return New JSONDocument ready to be saved to the backend
  254. */
  255. private JSONDocument makeDoc(Credentials cred, JSONObject definition, boolean forUpdate) throws DocumentCreationException {
  256. String username = definition.getString("username");
  257. String password = definition.getString("password");
  258. boolean sa = definition.optBoolean("is_sa", false);
  259. if(username.equals("anonymous"))
  260. sa = false;
  261. JSONDocument userdoc =
  262. (JSONDocument) Document.newDocument(
  263. memeDB.getBackend(),
  264. MemeDB.USERS_DB,
  265. username,
  266. MemeDB.CONTENT_JSON,
  267. cred.getUsername());
  268. userdoc.setRevisionData(definition);
  269. if(!forUpdate)
  270. userdoc.put("password", BCrypt.hashpw(password, BCrypt.gensalt()));
  271. userdoc.put("is_sa", sa);
  272. return userdoc;
  273. }
  274. }