/****************************************************************************** * * * Copyright 2017 Subterranean Security * * * * 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 com.subterranean_security.crimson.server.net.exe; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.subterranean_security.crimson.core.attribute.keys.AKeySimple; import com.subterranean_security.crimson.core.net.Connector; import com.subterranean_security.crimson.core.net.Connector.ConnectionState; import com.subterranean_security.crimson.core.net.MessageFuture; import com.subterranean_security.crimson.core.proto.Login.RQ_LoginChallenge; import com.subterranean_security.crimson.core.proto.Login.RS_Login; import com.subterranean_security.crimson.core.proto.MSG.Message; import com.subterranean_security.crimson.core.proto.Misc.Outcome; import com.subterranean_security.crimson.core.proto.SMSG.RS_CloudUser; import com.subterranean_security.crimson.core.store.ConnectionStore; import com.subterranean_security.crimson.core.util.RandomUtil; import com.subterranean_security.crimson.core.util.ValidationUtil; import com.subterranean_security.crimson.server.ServerState; import com.subterranean_security.crimson.server.store.ProfileStore; import com.subterranean_security.crimson.sv.permissions.Perm; import com.subterranean_security.crimson.sv.profile.ViewerProfile; import com.subterranean_security.crimson.universal.Universal; import com.subterranean_security.crimson.universal.stores.DatabaseStore; import com.subterranean_security.services.Services; public final class LoginExe { private static final Logger log = LoggerFactory.getLogger(LoginExe.class); private LoginExe() { } public static Outcome rq_login(Connector receptor, Message m) { long t1 = System.currentTimeMillis(); Outcome.Builder outcome = Outcome.newBuilder().setResult(false); log.debug("Processing login request from: " + receptor.getRemoteIP()); ViewerProfile vp = null; RS_CloudUser cloud = null; // by attempting to login, this receptor has revealed it is a viewer receptor.setInstance(Universal.Instance.VIEWER); // validate username String user = m.getRqLogin().getUsername(); if (!ValidationUtil.username(user)) return outcome.setResult(false).setComment("The provided username is invalid") .setTime(System.currentTimeMillis() - t1).build(); if (ServerState.isExampleMode()) { vp = new ViewerProfile(receptor.getCvid()); user = "user_" + Math.abs(RandomUtil.nextInt()); passLogin(receptor, m.getId(), vp); return outcome.setResult(true).setTime(System.currentTimeMillis() - t1).build(); } // find user vp = ProfileStore.getViewer(user); if (vp == null) { if (ServerState.isCloudMode()) { // check if the cloud server has this profile cloud = Services.getCloudUser(user); if (cloud != null) { // create ViewerProfile vp = new ViewerProfile(receptor.getCvid()); vp.set(AKeySimple.VIEWER_USER, user); vp.getPermissions().addFlag(Perm.server.generator.generate).addFlag(Perm.server.fs.read); ProfileStore.addViewer(vp); } } } // if profile is still not found if (vp == null) { return outcome.setResult(false).setComment("The provided user could not be found") .setTime(System.currentTimeMillis() - t1).build(); } Outcome authOutcome = (cloud == null) ? handleAuthentication(receptor, m, user) : handleCloudAuthentication(receptor, m, cloud); outcome.setResult(authOutcome.getResult()); if (outcome.getResult()) { passLogin(receptor, m.getId(), vp); } else { failLogin(receptor, m.getId(), vp, outcome.build()); } return outcome.setTime(System.currentTimeMillis() - t1).build(); } private static Outcome handleAuthentication(Connector receptor, Message m, String user) { long t1 = System.currentTimeMillis(); Outcome.Builder outcome = Outcome.newBuilder().setResult(false); try { log.debug("Issuing user challenge"); RQ_LoginChallenge.Builder challenge = RQ_LoginChallenge.newBuilder().setCloud(false); if (DatabaseStore.getDatabase().userExists(user)) { challenge.setSalt(DatabaseStore.getDatabase().getSalt(user)); } else { throw new Exception("Provided user could not be found"); } MessageFuture future = receptor .writeAndGetResponse(Message.newBuilder().setId(m.getId()).setRqLoginChallenge(challenge).build()); outcome.setResult( DatabaseStore.getDatabase().validLogin(user, future.get(5000).getRsLoginChallenge().getResult())); } catch (Exception e) { outcome.setResult(false).setComment(e.getMessage()); } return outcome.setTime(System.currentTimeMillis() - t1).build(); } private static Outcome handleCloudAuthentication(Connector receptor, Message m, RS_CloudUser cloud) { long t1 = System.currentTimeMillis(); Outcome.Builder outcome = Outcome.newBuilder().setResult(false); try { log.debug("Issuing cloud user challenge"); MessageFuture future = receptor.writeAndGetResponse(Message.newBuilder().setId(m.getId()) .setRqLoginChallenge(RQ_LoginChallenge.newBuilder().setCloud(true).setSalt(cloud.getSalt())) .build()); outcome.setResult(future.get(5000).getRsLoginChallenge().getResult().equals(cloud.getPassword())); } catch (Exception e) { outcome.setResult(false).setComment(e.getMessage()); } return outcome.setTime(System.currentTimeMillis() - t1).build(); } private static void passLogin(Connector receptor, int id, ViewerProfile vp) { try { log.debug("Accepting login: " + vp.get(AKeySimple.VIEWER_USER)); // this connection is now authenticated receptor.setState(ConnectionState.AUTHENTICATED); ConnectionStore.add(receptor); updateViewerProfile(receptor, vp); Date lastLogin = vp.getLastLoginTime(); receptor.write(Message.newBuilder().setId(id) .setRsLogin(RS_Login.newBuilder().setResponse(Outcome.newBuilder().setResult(true)) .setSpd(ProfileStore.getServer().getUpdates(lastLogin, vp))) .build()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void updateViewerProfile(Connector receptor, ViewerProfile vp) { vp.set(AKeySimple.VIEWER_LOGIN_IP, receptor.getRemoteIP()); vp.set(AKeySimple.VIEWER_LOGIN_TIME, new Date().toString()); } private static void failLogin(Connector receptor, int id, ViewerProfile vp, Outcome outcome) { log.debug("Rejecting login: " + vp.get(AKeySimple.VIEWER_USER)); receptor.write(Message.newBuilder().setId(id).setRsLogin(RS_Login.newBuilder().setResponse(outcome)).build()); receptor.close(); } }