package cc.blynk.integration.https; import cc.blynk.integration.BaseTest; import cc.blynk.integration.model.http.ResponseUserEntity; import cc.blynk.server.api.http.HttpAPIServer; import cc.blynk.server.api.http.HttpsAPIServer; import cc.blynk.server.core.BaseServer; import cc.blynk.server.core.model.AppName; import cc.blynk.server.core.model.auth.User; import cc.blynk.utils.JsonParser; import cc.blynk.utils.SHA256Util; import org.apache.http.Header; import org.apache.http.NameValuePair; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; import javax.net.ssl.*; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.*; /** * The Blynk Project. * Created by Dmitriy Dumanskiy. * Created on 24.12.15. */ @RunWith(MockitoJUnitRunner.class) public class HttpsAdminServerTest extends BaseTest { private static BaseServer httpServer; private BaseServer httpAdminServer; private CloseableHttpClient httpclient; private String httpsAdminServerUrl; private String httpServerUrl; private User admin; @After public void shutdown() { httpAdminServer.close(); httpServer.close(); } @Before public void init() throws Exception { this.httpAdminServer = new HttpsAPIServer(holder, false).start(); httpsAdminServerUrl = String.format("https://localhost:%s/admin", httpsPort); httpServerUrl = String.format("http://localhost:%s/", httpPort); SSLContext sslcontext = initUnsecuredSSLContext(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new MyHostVerifier()); this.httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()) .build(); httpServer = new HttpAPIServer(holder).start(); String name = "admin@blynk.cc"; String pass = "admin"; admin = new User(name, SHA256Util.makeHash(pass, name), AppName.BLYNK, "local", false, true); holder.userDao.add(admin); } @Override public String getDataFolder() { return getRelativeDataFolder("/profiles"); } private SSLContext initUnsecuredSSLContext() throws NoSuchAlgorithmException, KeyManagementException { X509TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException { } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException { } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } }; SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[]{ tm }, null); return context; } @Test public void testGetnOnExistingUser() throws Exception { String testUser = "dima@dima.ua"; HttpPut request = new HttpPut(httpsAdminServerUrl + "/users/" + "xxx/" + testUser); request.setEntity(new StringEntity(new ResponseUserEntity("123").toString(), ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(404, response.getStatusLine().getStatusCode()); } } @Test public void testGetWrongUrl() throws Exception { String testUser = "dima@dima.ua"; HttpPut request = new HttpPut(httpsAdminServerUrl + "/urs213213/" + "xxx/" + testUser); request.setEntity(new StringEntity(new ResponseUserEntity("123").toString(), ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(404, response.getStatusLine().getStatusCode()); } } @Test public void adminLoginFlowSupport() throws Exception { HttpGet loadLoginPageRequest = new HttpGet(httpsAdminServerUrl); try (CloseableHttpResponse response = httpclient.execute(loadLoginPageRequest)) { assertEquals(200, response.getStatusLine().getStatusCode()); String loginPage = consumeText(response); //todo add full page match? assertTrue(loginPage.contains("Use your Admin account to log in")); } login(admin.email, admin.pass); HttpGet loadAdminPage = new HttpGet(httpsAdminServerUrl); try (CloseableHttpResponse response = httpclient.execute(loadAdminPage)) { assertEquals(200, response.getStatusLine().getStatusCode()); String adminPage = consumeText(response); //todo add full page match? assertTrue(adminPage.contains("Blynk Administration")); assertTrue(adminPage.contains("admin.js")); } } @Test public void adminLoginOnlyForSuperUser() throws Exception { String name = "admin@blynk.cc"; String pass = "admin"; User admin = new User(name, SHA256Util.makeHash(pass, name), AppName.BLYNK, "local", false, false); holder.userDao.add(admin); HttpPost loginRequest = new HttpPost(httpsAdminServerUrl + "/login"); List <NameValuePair> nvps = new ArrayList<>(); nvps.add(new BasicNameValuePair("email", admin.email)); nvps.add(new BasicNameValuePair("password", admin.pass)); loginRequest.setEntity(new UrlEncodedFormEntity(nvps)); try (CloseableHttpResponse response = httpclient.execute(loginRequest)) { assertEquals(301, response.getStatusLine().getStatusCode()); Header header = response.getFirstHeader("Location"); assertNotNull(header); assertEquals("/admin", header.getValue()); Header cookieHeader = response.getFirstHeader("set-cookie"); assertNull(cookieHeader); } } @Test public void testGetUserFromAdminPageNoAccess() throws Exception { String testUser = "dmitriy@blynk.cc"; String appName = "Blynk"; HttpGet request = new HttpGet(httpsAdminServerUrl + "/users/" + testUser + "-" + appName); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(404, response.getStatusLine().getStatusCode()); } } @Test public void testGetUserFromAdminPageNoAccessWithFakeCookie() throws Exception { String testUser = "dmitriy@blynk.cc"; String appName = "Blynk"; HttpGet request = new HttpGet(httpsAdminServerUrl + "/users/" + testUser + "-" + appName); request.setHeader("set-cookie", "session=123"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(404, response.getStatusLine().getStatusCode()); } } @Test public void testGetUserFromAdminPage() throws Exception { login(admin.email, admin.pass); String testUser = "dmitriy@blynk.cc"; String appName = "Blynk"; HttpGet request = new HttpGet(httpsAdminServerUrl + "/users/" + testUser + "-" + appName); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String jsonProfile = consumeText(response); assertNotNull(jsonProfile); User user = JsonParser.readAny(jsonProfile, User.class); assertNotNull(user); assertEquals(testUser, user.email); assertNotNull(user.profile.dashBoards); assertEquals(5, user.profile.dashBoards.length); } } private void login(String name, String pass) throws Exception { HttpPost loginRequest = new HttpPost(httpsAdminServerUrl + "/login"); List <NameValuePair> nvps = new ArrayList<>(); nvps.add(new BasicNameValuePair("email", name)); nvps.add(new BasicNameValuePair("password", pass)); loginRequest.setEntity(new UrlEncodedFormEntity(nvps)); try (CloseableHttpResponse response = httpclient.execute(loginRequest)) { assertEquals(301, response.getStatusLine().getStatusCode()); Header header = response.getFirstHeader("Location"); assertNotNull(header); assertEquals("/admin", header.getValue()); Header cookieHeader = response.getFirstHeader("set-cookie"); assertNotNull(cookieHeader); assertTrue(cookieHeader.getValue().startsWith("session=")); } } @Test public void testChangeUsernameChangesPassToo() throws Exception { login(admin.email, admin.pass); User user; HttpGet getUserRequest = new HttpGet(httpsAdminServerUrl + "/users/admin@blynk.cc-Blynk"); try (CloseableHttpResponse response = httpclient.execute(getUserRequest)) { assertEquals(200, response.getStatusLine().getStatusCode()); String userProfile = consumeText(response); assertNotNull(userProfile); user = JsonParser.parseUserFromString(userProfile); assertEquals(admin.email, user.email); } user.email = "123@blynk.cc"; //we are no allowed to change username without cahnged password HttpPut changeUserNameRequestWrong = new HttpPut(httpsAdminServerUrl + "/users/admin@blynk.cc-Blynk"); changeUserNameRequestWrong.setEntity(new StringEntity(user.toString(), ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(changeUserNameRequestWrong)) { assertEquals(400, response.getStatusLine().getStatusCode()); } user.pass = "123"; HttpPut changeUserNameRequestCorrect = new HttpPut(httpsAdminServerUrl + "/users/admin@blynk.cc-Blynk"); changeUserNameRequestCorrect.setEntity(new StringEntity(user.toString(), ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(changeUserNameRequestCorrect)) { assertEquals(200, response.getStatusLine().getStatusCode()); } HttpGet getNonExistingUserRequest = new HttpGet(httpsAdminServerUrl + "/users/admin@blynk.cc-Blynk"); try (CloseableHttpResponse response = httpclient.execute(getNonExistingUserRequest)) { assertEquals(404, response.getStatusLine().getStatusCode()); } HttpGet getUserRequest2 = new HttpGet(httpsAdminServerUrl + "/users/123@blynk.cc-Blynk"); try (CloseableHttpResponse response = httpclient.execute(getUserRequest2)) { assertEquals(200, response.getStatusLine().getStatusCode()); String userProfile = consumeText(response); assertNotNull(userProfile); user = JsonParser.parseUserFromString(userProfile); assertEquals("123@blynk.cc", user.email); assertEquals(SHA256Util.makeHash("123", user.email), user.pass); } } @Test public void testGetAdminPage() throws Exception { HttpGet request = new HttpGet(httpsAdminServerUrl); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } } @Test public void testGetFavIconHttps() throws Exception { HttpGet request = new HttpGet(httpsAdminServerUrl.replace("/admin", "") + "/favicon.ico"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } } @Test public void getStaticFile() throws Exception { HttpGet request = new HttpGet(httpsAdminServerUrl.replace("admin", "static/admin.html")); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } } @Test public void testGetFavIconHttp() throws Exception { HttpGet request = new HttpGet(httpServerUrl + "favicon.ico"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } } @Test public void testAssignNewTokenForNonExistingToken() throws Exception { login(admin.email, admin.pass); HttpGet request = new HttpGet(httpsAdminServerUrl + "/users/token/assign?old=123&new=123"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(400, response.getStatusLine().getStatusCode()); } } @Test public void testAssignNewToken() throws Exception { login(admin.email, admin.pass); HttpGet request = new HttpGet(httpsAdminServerUrl + "/users/token/assign?old=4ae3851817194e2596cf1b7103603ef8&new=123"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } HttpPut put = new HttpPut(httpServerUrl + "123/pin/v10"); put.setEntity(new StringEntity("[\"100\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(put)) { assertEquals(200, response.getStatusLine().getStatusCode()); } HttpGet get = new HttpGet(httpServerUrl + "123/pin/v10"); try (CloseableHttpResponse response = httpclient.execute(get)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("100", values.get(0)); } request = new HttpGet(httpsAdminServerUrl + "/users/token/assign?old=4ae3851817194e2596cf1b7103603ef8&new=124"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(400, response.getStatusLine().getStatusCode()); } } @Test public void testForceAssignNewToken() throws Exception { login(admin.email, admin.pass); HttpGet request = new HttpGet(httpsAdminServerUrl + "/users/token/force?email=dmitriy@blynk.cc&app=Blynk&dashId=79780619&deviceId=0&new=123"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } HttpPut put = new HttpPut(httpServerUrl + "123/pin/v10"); put.setEntity(new StringEntity("[\"100\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(put)) { assertEquals(200, response.getStatusLine().getStatusCode()); } HttpGet get = new HttpGet(httpServerUrl + "123/pin/v10"); try (CloseableHttpResponse response = httpclient.execute(get)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("100", values.get(0)); } } private class MyHostVerifier implements HostnameVerifier { @Override public boolean verify(String s, SSLSession sslSession) { return true; } } }