package dev.peerat.backend.routes.users; import static dev.peerat.framework.RequestType.POST; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Constructor; import java.net.URL; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.Base64; import java.util.Base64.Encoder; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; import java.util.regex.Matcher; import javax.net.ssl.HttpsURLConnection; import org.jose4j.json.internal.json_simple.JSONAware; import org.jose4j.json.internal.json_simple.JSONObject; import dev.peerat.backend.bonus.extract.RouteDoc; import dev.peerat.backend.model.PeerAtUser; import dev.peerat.backend.repository.DatabaseRepository; import dev.peerat.backend.utils.FormResponse; import dev.peerat.backend.utils.Mail; import dev.peerat.framework.Context; import dev.peerat.framework.HttpReader; import dev.peerat.framework.HttpWriter; import dev.peerat.framework.Injection; import dev.peerat.framework.Route; import dev.peerat.framework.Router; import dev.peerat.framework.utils.json.JsonMap; public class MailConfirmation extends FormResponse{ private DatabaseRepository databaseRepo; private Router router; private String usersFilesPath; private KeyPairGenerator generator; private Encoder encoder; private String gitToken; private Map playersWaiting; private Mail mail; private List randoms; public MailConfirmation( DatabaseRepository databaseRepo, Router router, @Injection("usersFiles") String initUsersFilesPath, @Injection("gitToken") String gitToken, @Injection("waitting") Map playersWaiting, Mail mail) throws NoSuchAlgorithmException{ this.databaseRepo = databaseRepo; this.router = router; this.usersFilesPath = initUsersFilesPath; this.gitToken = gitToken; this.playersWaiting = playersWaiting; this.mail = mail; generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(4096); encoder = Base64.getEncoder(); this.randoms = new ArrayList<>(); Random random = new Random(); int randoms = random.nextInt(10)+3; for(int i = 0; i < randoms; i++) this.randoms.add(new SecureRandom()); try { Constructor constructor = UUID.class.getDeclaredConstructor(byte[].class); constructor.setAccessible(true); this.uuidBuilder = constructor; } catch (Exception e){ e.printStackTrace(); } validator("pseudo", "[a-zA-Z0-9&|!?{}\\[\\]%/*\\-+=:;,_#@ ]{3,100}"); validator("firstname", "^(?>[a-zA-Z]+ ?)+$"); validator("lastname", "^(?>[a-zA-Z]+ ?)+$"); } @RouteDoc(path = "/confirmation", responseCode = 200, responseDescription = "L'utilisateur est inscrit") @RouteDoc(responseCode = 403, responseDescription = "L'utilisateur est connecté") @RouteDoc(responseCode = 400, responseDescription = "Aucune données fournie / données invalide") @Route(path = "^\\/confirmation$", type = POST) public void exec(Matcher matcher, Context context, HttpReader reader, HttpWriter writer) throws Exception { if(context.isLogged()){ context.response(403); return; } JsonMap json = json(reader); if((!areValids("email","pseudo","firstname","lastname","passwd")) || (!hasFields("code"))){ context.response(400); return; } String email = json.get("email"); String code = json.get("code"); String pseudo = json.get("pseudo"); String firstname = json.get("firstname"); String lastname = json.get("lastname"); String password = json.get("passwd"); String checkCode = playersWaiting.get(email); if(checkCode == null){ context.response(400); return; } boolean pseudoAvailable = databaseRepo.checkPseudoAvailability(pseudo); boolean emailAvailable = databaseRepo.checkEmailAvailability(email); if(pseudoAvailable && emailAvailable){ if(code.equals(checkCode)){ playersWaiting.remove(email); int id = databaseRepo.register(pseudo, email, password, firstname, lastname, "", "", ""); if(id >= 0){ // createFolderToSaveSourceCode(pseudo); // generateGitKey(email, pseudo, password); context.response(200, "Access-Control-Expose-Headers: Authorization", "Authorization: Bearer " + this.router.createAuthUser(new PeerAtUser(id))); }else{ context.response(400); JsonMap error = new JsonMap(); error.set("username_valid", pseudo); error.set("email_valid", email); writer.write(error.toString()); String ncode = codeGenerator()+codeGenerator(); playersWaiting.put(email, ncode); mail.send(email, "Welcome @ Peer @ Code", "Your check code is "+ncode+" !"); } }else{ context.response(400); } }else{ context.response(400); JsonMap error = new JsonMap(); error.set("username_valid", pseudo); error.set("email_valid", email); writer.write(error.toString()); } } private Constructor uuidBuilder; private String codeGenerator() throws Exception{ Random random = new Random(); Random target = this.randoms.get(random.nextInt(this.randoms.size())); byte[] arrayOfByte = new byte[16]; target.nextBytes(arrayOfByte); arrayOfByte[6] = (byte)(arrayOfByte[6] & 0xF); arrayOfByte[6] = (byte)(arrayOfByte[6] | 0x40); arrayOfByte[8] = (byte)(arrayOfByte[8] & 0x3F); arrayOfByte[8] = (byte)(arrayOfByte[8] | 0x80); return uuidBuilder.newInstance(arrayOfByte).toString(); } private void createFolderToSaveSourceCode(String pseudo) throws IOException { Files.createDirectories(Paths.get(String.format("%s/%s", usersFilesPath, pseudo))); } private static byte[] PREFIX = new byte[] {0,0,0,7,(byte)'s',(byte)'s',(byte)'h',(byte)'-',(byte)'r',(byte)'s',(byte)'a'}; private String generateGitKey(String email, String pseudo, String password) throws Exception{ KeyPair pair = generator.generateKeyPair(); //doit être unique ??? JSONObject createUser = new JSONObject(); createUser.put("email", email); createUser.put("username", pseudo); createUser.put("password", password); post("https://git-users.peerat.dev/api/v1/admin/users/", createUser); JSONObject sendKey = new JSONObject(); RSAPublicKey pub = (RSAPublicKey) pair.getPublic(); byte[] exponent = pub.getPublicExponent().toByteArray(); byte[] modulus = pub.getModulus().toByteArray(); byte[] key = new byte[19+exponent.length+modulus.length]; System.arraycopy(PREFIX, 0, key, 0, 11); byte[] exLength = ByteBuffer.allocate(4).putInt(exponent.length).array(); byte[] modLength = ByteBuffer.allocate(4).putInt(modulus.length).array(); System.arraycopy(exLength, 0, key, 11, 4); System.arraycopy(exponent, 0, key, 15, exponent.length); System.arraycopy(modLength, 0, key, 15+exponent.length, 4); System.arraycopy(modulus, 0, key, 19+exponent.length, modulus.length); sendKey.put("key", "ssh-rsa "+new String(encoder.encode(key))); sendKey.put("read_only", false); sendKey.put("title", "peer_at_code_auto_push_key_"+pseudo); post("https://git-users.peerat.dev/api/v1/admin/users/"+pseudo+"/keys", sendKey); return new String(encoder.encode(pair.getPrivate().getEncoded())); } private void post(String url, JSONAware json) throws Exception{ HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type","application/json"); connection.setRequestProperty("Authorization","Bearer "+this.gitToken); connection.setDoInput(true); connection.setDoOutput(true); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream())); writer.write(json.toJSONString()); writer.flush(); writer.close(); int response = connection.getResponseCode(); if(response != 201) throw new Exception("Call to "+url+" failed with response code "+response); } }