peer-at-code-backend/src/dev/peerat/backend/routes/users/MailConfirmation.java

230 lines
8.3 KiB
Java

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.Configuration;
import dev.peerat.backend.bonus.extract.RouteDoc;
import dev.peerat.backend.model.PeerAtUser;
import dev.peerat.backend.repository.DatabaseAuthRepository;
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 DatabaseAuthRepository databaseRepo;
private Router<PeerAtUser> router;
private String usersFilesPath;
private KeyPairGenerator generator;
private Encoder encoder;
private String gitToken;
private Map<String, String> playersWaiting;
private Mail mail;
private List<Random> randoms;
public MailConfirmation(
DatabaseRepository databaseRepo,
Router<PeerAtUser> router,
Configuration config,
@Injection("waitting") Map<String, String> playersWaiting,
Mail mail) throws NoSuchAlgorithmException{
this.databaseRepo = databaseRepo.getAuthRepository();
this.router = router;
this.usersFilesPath = config.getUsersFiles();
this.gitToken = config.getGitToken();
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.<String>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();
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 int[] start = {4, 9, 14, 19};
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);
String uuid = uuidBuilder.newInstance(arrayOfByte).toString();
target = this.randoms.get(random.nextInt(this.randoms.size()));
int start = this.start[target.nextInt(this.start.length)];
return uuid.substring(start, start+9);
}
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);
}
}