<commit_before>package org.pocketcampus.plugin.authentication.server;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import org.apache.thrift.TException;
import org.pocketcampus.platform.server.RawPlugin;
import org.pocketcampus.platform.server.launcher.PocketCampusServer;
import org.pocketcampus.platform.shared.PCConstants;
import org.pocketcampus.platform.shared.utils.StringUtils;
import org.pocketcampus.plugin.authentication.shared.AuthSessionRequest;
import org.pocketcampus.plugin.authentication.shared.AuthSessionResponse;
import org.pocketcampus.plugin.authentication.shared.AuthStatusCode;
import org.pocketcampus.plugin.authentication.shared.AuthTokenResponse;
import org.pocketcampus.plugin.authentication.shared.AuthenticationService;
import org.pocketcampus.plugin.authentication.shared.LogoutRequest;
import org.pocketcampus.plugin.authentication.shared.LogoutResponse;
import org.pocketcampus.plugin.authentication.shared.UserAttributesRequest;
import org.pocketcampus.plugin.authentication.shared.UserAttributesResponse;
import org.pocketcampus.plugin.authentication.shared.AuthenticationConstants;

import ch.epfl.tequila.client.model.ClientConfig;
import ch.epfl.tequila.client.model.TequilaPrincipal;
import ch.epfl.tequila.client.service.TequilaService;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
 * AuthenticationServiceImpl
 * 
 * The implementation of the server side of the Authentication Plugin.
 * 
 * It authenticates users to the PocketCampus service.
 * 
 * @author Amer <<EMAIL>>
 * 
 */
public class AuthenticationServiceImpl implements AuthenticationService.Iface, RawPlugin {

	private final SessionManager _manager;

	public AuthenticationServiceImpl() {
		System.out.println("Starting Authentication plugin server ...");
		_manager = new SessionManagerOAuth2();
	}


	@Override
	public HttpServlet getServlet() {
		return new HttpServlet() {


			class ConfigRequest {
				long timestamp = System.currentTimeMillis();
				String sess;
				String email;
				String gaspar;
				String sciper;
				String lang; // can be null
				String type; // email or vpn
				String getfile;
			}

			Map<String, ConfigRequest> map = new ConcurrentHashMap<String, ConfigRequest>();

			private void cleanup() {
				Iterator<Map.Entry<String, ConfigRequest>> i = map.entrySet().iterator();
				while(i.hasNext()) {
					Map.Entry<String, ConfigRequest> e = i.next();
					if(System.currentTimeMillis() - e.getValue().timestamp > 60000) {
						i.remove();
					}
				}
			}

			private void returnPage(HttpServletResponse resp, int status, String body) throws IOException {
				resp.setStatus(status);
				resp.setContentType("text/html");
				resp.setCharacterEncoding("UTF-8");
				OutputStream o = resp.getOutputStream();
				o.write(body.getBytes("UTF-8"));
				o.close();
			}

			@Override
			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
				cleanup();

				ConfigRequest cr = new ConfigRequest();
				cr.sess = req.getHeader(PCConstants.HTTP_HEADER_AUTH_PCSESSID);
				if(cr.sess != null) {
					cr.email = getFieldFromSession(cr.sess, "`email`");
					cr.gaspar = getFieldFromSession(cr.sess, "`gaspar`");
					cr.sciper = getFieldFromSession(cr.sess, "`sciper`");
				}
				cr.lang = req.getHeader(PCConstants.HTTP_HEADER_USER_LANG_CODE);
				cr.type = req.getParameter("config");
				cr.getfile = req.getParameter("getfile");
				if(cr.getfile != null) {
					ConfigRequest mapped = map.get(cr.getfile);
					if(mapped == null) {
						resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
						return;
					}
					cr = mapped;
				}


				if (cr.sess == null || cr.gaspar == null || cr.email == null || cr.sciper == null) {
					resp.setStatus(HttpURLConnection.HTTP_PROXY_AUTH);
					return;
				}


				String pemFile = PocketCampusServer.CONFIG.getString("IOS_PROV_PROFILE_SIGNING_PEM_FILE");

				Map<String, String> xml = IosProvisionningProfiles.XML_MAP.get(cr.type);

				if(xml == null) {
					resp.setStatus(HttpURLConnection.HTTP_BAD_REQUEST);
					return;

				}



				String nameForUuid = "ios epfl config " + cr.type + " " + cr.sciper;

				UUID configurationPayloadUuid = UUID.nameUUIDFromBytes((nameForUuid + " configuration payload uuid").getBytes());
				UUID accountPayloadUuid = UUID.nameUUIDFromBytes((nameForUuid + " account payload uuid").getBytes());

				String body = xml.get(cr.lang);
				if(body == null) {
					body = xml.get("en");
				}
				body = body.replace("USER_EMAIL", cr.email)
						.replace("USER_GASPAR", cr.gaspar)
						.replace("USER_SCIPER", cr.sciper)
						.replace("CONFIGURATION_PAYLOAD_UUID", configurationPayloadUuid.toString())
						.replace("ACCOUNT_PAYLOAD_UUID", accountPayloadUuid.toString());

				if(cr.getfile == null) {
					cr.getfile = UUID.randomUUID().toString();
					map.put(cr.getfile, cr);
					JsonObject reply = new JsonObject();
					reply.addProperty("getfile", cr.getfile);
					resp.setContentType("application/json");
					resp.setCharacterEncoding("UTF-8");
					resp.getOutputStream().write(new Gson().toJson(reply).getBytes("UTF-8"));

				} else {
					map.remove(cr.getfile);

					if("vpn".equals(cr.type)) {
						returnPage(resp, HttpURLConnection.HTTP_UNAVAILABLE, "<h1>Service Unavailable</h1>" +
								"<p>Sorry, VPN configuration is currently unavailable.</p>");
						return;
					}

					resp.setContentType("application/x-apple-aspen-config");
					resp.setCharacterEncoding("UTF-8");
					resp.setHeader("Content-Disposition", "attachment; filename=\"pocketcampus.mobileconfig\"");
					IosProvisionningProfiles.sign(pemFile, body, resp.getOutputStream());
				}<commit_msg>[authentication.server] improved service unavailable message for VPN config<commit_after>package org.pocketcampus.plugin.authentication.server;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import org.apache.thrift.TException;
import org.pocketcampus.platform.server.RawPlugin;
import org.pocketcampus.platform.server.launcher.PocketCampusServer;
import org.pocketcampus.platform.shared.PCConstants;
import org.pocketcampus.platform.shared.utils.StringUtils;
import org.pocketcampus.plugin.authentication.shared.AuthSessionRequest;
import org.pocketcampus.plugin.authentication.shared.AuthSessionResponse;
import org.pocketcampus.plugin.authentication.shared.AuthStatusCode;
import org.pocketcampus.plugin.authentication.shared.AuthTokenResponse;
import org.pocketcampus.plugin.authentication.shared.AuthenticationService;
import org.pocketcampus.plugin.authentication.shared.LogoutRequest;
import org.pocketcampus.plugin.authentication.shared.LogoutResponse;
import org.pocketcampus.plugin.authentication.shared.UserAttributesRequest;
import org.pocketcampus.plugin.authentication.shared.UserAttributesResponse;
import org.pocketcampus.plugin.authentication.shared.AuthenticationConstants;

import ch.epfl.tequila.client.model.ClientConfig;
import ch.epfl.tequila.client.model.TequilaPrincipal;
import ch.epfl.tequila.client.service.TequilaService;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
 * AuthenticationServiceImpl
 * 
 * The implementation of the server side of the Authentication Plugin.
 * 
 * It authenticates users to the PocketCampus service.
 * 
 * @author Amer <<EMAIL>>
 * 
 */
public class AuthenticationServiceImpl implements AuthenticationService.Iface, RawPlugin {

	private final SessionManager _manager;

	public AuthenticationServiceImpl() {
		System.out.println("Starting Authentication plugin server ...");
		_manager = new SessionManagerOAuth2();
	}


	@Override
	public HttpServlet getServlet() {
		return new HttpServlet() {


			class ConfigRequest {
				long timestamp = System.currentTimeMillis();
				String sess;
				String email;
				String gaspar;
				String sciper;
				String lang; // can be null
				String type; // email or vpn
				String getfile;
			}

			Map<String, ConfigRequest> map = new ConcurrentHashMap<String, ConfigRequest>();

			private void cleanup() {
				Iterator<Map.Entry<String, ConfigRequest>> i = map.entrySet().iterator();
				while(i.hasNext()) {
					Map.Entry<String, ConfigRequest> e = i.next();
					if(System.currentTimeMillis() - e.getValue().timestamp > 60000) {
						i.remove();
					}
				}
			}

			private void returnPage(HttpServletResponse resp, int status, String body) throws IOException {
				resp.setStatus(status);
				resp.setContentType("text/html");
				resp.setCharacterEncoding("UTF-8");
				OutputStream o = resp.getOutputStream();
				o.write(body.getBytes("UTF-8"));
				o.close();
			}

			@Override
			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
				cleanup();

				ConfigRequest cr = new ConfigRequest();
				cr.sess = req.getHeader(PCConstants.HTTP_HEADER_AUTH_PCSESSID);
				if(cr.sess != null) {
					cr.email = getFieldFromSession(cr.sess, "`email`");
					cr.gaspar = getFieldFromSession(cr.sess, "`gaspar`");
					cr.sciper = getFieldFromSession(cr.sess, "`sciper`");
				}
				cr.lang = req.getHeader(PCConstants.HTTP_HEADER_USER_LANG_CODE);
				cr.type = req.getParameter("config");
				cr.getfile = req.getParameter("getfile");
				if(cr.getfile != null) {
					ConfigRequest mapped = map.get(cr.getfile);
					if(mapped == null) {
						resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
						return;
					}
					cr = mapped;
				}


				if (cr.sess == null || cr.gaspar == null || cr.email == null || cr.sciper == null) {
					resp.setStatus(HttpURLConnection.HTTP_PROXY_AUTH);
					return;
				}


				String pemFile = PocketCampusServer.CONFIG.getString("IOS_PROV_PROFILE_SIGNING_PEM_FILE");

				Map<String, String> xml = IosProvisionningProfiles.XML_MAP.get(cr.type);

				if(xml == null) {
					resp.setStatus(HttpURLConnection.HTTP_BAD_REQUEST);
					return;

				}



				String nameForUuid = "ios epfl config " + cr.type + " " + cr.sciper;

				UUID configurationPayloadUuid = UUID.nameUUIDFromBytes((nameForUuid + " configuration payload uuid").getBytes());
				UUID accountPayloadUuid = UUID.nameUUIDFromBytes((nameForUuid + " account payload uuid").getBytes());

				String body = xml.get(cr.lang);
				if(body == null) {
					body = xml.get("en");
				}
				body = body.replace("USER_EMAIL", cr.email)
						.replace("USER_GASPAR", cr.gaspar)
						.replace("USER_SCIPER", cr.sciper)
						.replace("CONFIGURATION_PAYLOAD_UUID", configurationPayloadUuid.toString())
						.replace("ACCOUNT_PAYLOAD_UUID", accountPayloadUuid.toString());

				if(cr.getfile == null) {
					cr.getfile = UUID.randomUUID().toString();
					map.put(cr.getfile, cr);
					JsonObject reply = new JsonObject();
					reply.addProperty("getfile", cr.getfile);
					resp.setContentType("application/json");
					resp.setCharacterEncoding("UTF-8");
					resp.getOutputStream().write(new Gson().toJson(reply).getBytes("UTF-8"));

				} else {
					map.remove(cr.getfile);

					if("vpn".equals(cr.type)) {
						returnPage(resp, HttpURLConnection.HTTP_UNAVAILABLE, "<h1>Service unavailable</h1>" +
								"<p>Sorry, VPN automated configuration is currently unavailable.<br>More here: <a href='http://epnet.epfl.ch/files/content/sites/network/files/Download/VPN/anyconnect-iOS.pdf'>Instruction</a></p>");
						return;
					}

					resp.setContentType("application/x-apple-aspen-config");
					resp.setCharacterEncoding("UTF-8");
					resp.setHeader("Content-Disposition", "attachment; filename=\"pocketcampus.mobileconfig\"");
					IosProvisionningProfiles.sign(pemFile, body, resp.getOutputStream());
				}
{'fraction_non_alphanumeric': 0.12417769836757898, 'fraction_numerical': 0.0021115893770811338, 'mean_word_length': 4.3539130434782605, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1470610', 'n_tokens_mistral': 4451, 'n_tokens_neox': 3738, 'n_words': 651}