/* * Copyright (C) 2014 Divide.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.divide.shared.server; import io.divide.shared.transitory.Credentials; import io.divide.shared.transitory.TransientObject; import io.divide.shared.transitory.query.OPERAND; import io.divide.shared.transitory.query.Query; import io.divide.shared.transitory.query.QueryBuilder; import io.divide.shared.util.AuthTokenUtils; import io.divide.shared.util.AuthTokenUtils.AuthenticationException; import io.divide.shared.util.DaoUtils; import io.divide.shared.util.ObjectUtils; import io.divide.shared.util.ReflectionUtils; import org.apache.http.HttpStatus; import org.mindrot.jbcrypt.BCrypt; import java.security.PublicKey; import java.util.Calendar; import java.util.Map; import java.util.TimeZone; import static io.divide.shared.server.DAO.DAOException; import static io.divide.shared.util.DaoUtils.getUserByEmail; public class AuthServerLogic<DAOOut extends TransientObject> extends ServerLogic<DAOOut> { private static Calendar c = Calendar.getInstance(TimeZone.getDefault()); private KeyManager keyManager; public AuthServerLogic(DAO<TransientObject,DAOOut> dao, KeyManager keyManager) { super(dao); this.keyManager = keyManager; } /* * Saves user credentials */ public Credentials userSignUp(Credentials credentials) throws DAOException{ if (getUserByEmail(dao,credentials.getEmailAddress())!=null){ throw new DAOException(HttpStatus.SC_CONFLICT,"User Already Exists"); } ServerCredentials toSave = new ServerCredentials(credentials); toSave.decryptPassword(keyManager.getPrivateKey()); //decrypt the password String de = toSave.getPassword(); String ha = BCrypt.hashpw(de, BCrypt.gensalt(10)); toSave.setOwnerId(dao.count(Credentials.class.getName()) + 1); toSave.setPassword(ha); //hash the password for storage toSave.setAuthToken(AuthTokenUtils.getNewToken(keyManager.getSymmetricKey(), toSave)); toSave.setRecoveryToken(AuthTokenUtils.getNewToken(keyManager.getSymmetricKey(), toSave)); dao.save(toSave); return toSave; } /** * Checks username/password against that stored in DB, if same return * token, if token expired create new. * @param credentials * @return authentication token */ public Credentials userSignIn(Credentials credentials) throws DAOException { Credentials dbCreds = getUserByEmail(dao,credentials.getEmailAddress()); if (dbCreds == null){ throw new DAOException(HttpStatus.SC_UNAUTHORIZED,"User Doesnt exist"); } else { //check if we are resetting the password if(dbCreds.getValidation()!=null && dbCreds.getValidation().equals(credentials.getValidation())){ credentials.decryptPassword(keyManager.getPrivateKey()); //decrypt the password dbCreds.setPassword(BCrypt.hashpw(credentials.getPassword(), BCrypt.gensalt(10))); //set the new password } //else check password else { String en = credentials.getPassword(); credentials.decryptPassword(keyManager.getPrivateKey()); //decrypt the password String de = credentials.getPassword(); String ha = BCrypt.hashpw(de, BCrypt.gensalt(10)); System.out.println("Comparing passwords.\n" + "Encrypted: " + en + "\n" + "Decrypted: " + de + "\n" + "Hashed: " + ha + "\n" + "Stored: " + dbCreds.getPassword()); if (!BCrypt.checkpw(de, dbCreds.getPassword())){ throw new DAOException(HttpStatus.SC_UNAUTHORIZED,"User Already Exists"); } } // check if token is expired, if so return/set new AuthTokenUtils.AuthToken token; try { token = new AuthTokenUtils.AuthToken(keyManager.getSymmetricKey(),dbCreds.getAuthToken()); } catch (AuthenticationException e) { throw new DAOException(HttpStatus.SC_INTERNAL_SERVER_ERROR,"internal error"); } if (c.getTime().getTime() > token.expirationDate) { dbCreds.setAuthToken(AuthTokenUtils.getNewToken(keyManager.getSymmetricKey(), dbCreds)); dao.save(dbCreds); } return dbCreds; } } public byte[] getPublicKey() { PublicKey publicKey = keyManager.getPublicKey(); return publicKey.getEncoded(); } /** * Validate a user account * @param token */ public boolean validateAccount(String token) throws DAOException { Query q = new QueryBuilder().select().from(Credentials.class).where("validation", OPERAND.EQ, token).build(); TransientObject to = ObjectUtils.get1stOrNull(dao.query(q)); if (to != null) { ServerCredentials creds = new ServerCredentials(to); creds.setValidation("1"); dao.save(creds); return true; } else { return false; } } public Credentials getUserFromAuthToken(String token) throws DAOException { AuthTokenUtils.AuthToken authToken; try { authToken = new AuthTokenUtils.AuthToken(keyManager.getSymmetricKey(),token); } catch (AuthenticationException e) { throw new DAOException(HttpStatus.SC_INTERNAL_SERVER_ERROR,"internal error"); } if(authToken.isExpired()) throw new DAOException(HttpStatus.SC_UNAUTHORIZED,"Expired"); Query q = new QueryBuilder().select().from(Credentials.class).where(Credentials.AUTH_TOKEN_KEY,OPERAND.EQ,token).build(); TransientObject to = ObjectUtils.get1stOrNull(dao.query(q)); if(to!=null){ return new ServerCredentials(to); } else { throw new DAOException(HttpStatus.SC_BAD_REQUEST,"invalid auth token"); } } public Credentials getUserFromRecoveryToken(String token) throws DAOException { Query q = new QueryBuilder().select().from(Credentials.class).where(Credentials.RECOVERY_TOKEN_KEY,OPERAND.EQ,token).build(); TransientObject to = ObjectUtils.get1stOrNull(dao.query(q)); if(to!=null){ ServerCredentials sc = new ServerCredentials(to); sc.setAuthToken(AuthTokenUtils.getNewToken(keyManager.getSymmetricKey(), sc)); sc.setRecoveryToken(AuthTokenUtils.getNewToken(keyManager.getSymmetricKey(), sc)); dao.save(sc); return sc; } else { throw new DAOException(HttpStatus.SC_BAD_REQUEST,"invalid recovery token"); } } public void recieveUserData(String userId, Map<String,?> data) throws DAOException { Credentials user = getUserById(userId); user.removeAll(); user.putAll(data); dao.save(user); } public Map<String,Object> sendUserData(String userId) { return getUserById(userId).getUserData(); } public Credentials getUserById(String id){ return DaoUtils.getUserById(dao, id); } //// @POST //// @Path("/reset") //// @Consumes(MediaType.APPLICATION_JSON) //// @Produces(MediaType.APPLICATION_JSON) //// public Response resetAccount(EncryptedEntity encrypted) { //// try{ //// String email = encrypted.getPlainText(getKeys().getPrivate()); //// Query q = new QueryBuilder().select(null).from(Credentials.class).where("emailAddress", OPERAND.EQ, email).limit(1).build(); //// //// TransientObject to = (TransientObject) ObjectUtils.get1stOrNull(dao.query(q)); //// if (to != null) { //// ServerCredentials creds = new ServerCredentials(to); //// creds.setValidation(getNewAuthToken()); //// dao.save(creds); //// //// EmailMessage emailMessage = new EmailMessage( //// "someEmail", //// email, //// "Tactics Password Reset", //// "some link " + creds.getAuthToken()); //// //// sendEmail(emailMessage); //// //// return Response //// .ok() //// .build(); //// } else { //// return Response //// .status(Status.NOT_FOUND) //// .build(); //// } //// } catch (NoSuchAlgorithmException e) { //// return Response.serverError().build(); //// } catch (DAO.DAOException e) { //// return fromDAOExpection(e); //// } catch (MessagingException e) { //// return errorResponse(e); //// } catch (UnsupportedEncodingException e) { //// return errorResponse(e); //// } //// } // // private static Response errorResponse(Throwable error){ // return Response.status(Status.INTERNAL_SERVER_ERROR).entity(error).build(); // } // public void sendEmail(EmailMessage emailMessage) throws MessagingException, UnsupportedEncodingException { // // Properties props = new Properties(); // Session session = Session.getDefaultInstance(props, null); // // Message msg = new MimeMessage(session); // msg.setFrom(new InternetAddress(emailMessage.getFrom(), "")); // msg.addRecipient(Message.RecipientType.TO, new InternetAddress(emailMessage.getTo(), "")); // msg.setSubject(emailMessage.getSubject()); // msg.setText(emailMessage.getBody()); // Transport.send(msg); // // } // // public static class EmailMessage { // private String from; // private String to; // private String subject; // private String body; // // public EmailMessage(String from, String to, String subject, String body) throws MessagingException { // setFrom(from); // setTo(to); // setSubject(subject); // setBody(body); // } // // public String getFrom() { // return from; // } // // public void setFrom(String from) throws MessagingException { // if (!validEmail(from)) throw new MessagingException("Invalid email address!"); // this.from = from; // } // // public String getTo() { // return to; // } // // public void setTo(String to) throws MessagingException { // if (!validEmail(to)) throw new MessagingException("Invalid email address!"); // this.to = to; // } // // public String getSubject() { // return subject; // } // // public void setSubject(String subject) { // this.subject = subject; // } // // public String getBody() { // return body; // } // // public void setBody(String body) { // this.body = body; // } // // private boolean validEmail(String email) { // // editing to make requirements listed // // return email.matches("[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}"); // return email.matches("[A-Z0-9._%+-][A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{3}"); // } // } private static class ServerCredentials extends Credentials { public ServerCredentials(TransientObject serverObject){ try { Map meta = (Map) ReflectionUtils.getObjectField(serverObject, TransientObject.META_DATA); Map user = (Map) ReflectionUtils.getObjectField(serverObject,TransientObject.USER_DATA); ReflectionUtils.setObjectField(this, TransientObject.META_DATA, meta); ReflectionUtils.setObjectField(this, TransientObject.USER_DATA, user); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } @Override public void setOwnerId(Integer id){ super.setOwnerId(id); } } }