From 6d050ac11e08aece45424525a30c3ef2d5d7b156 Mon Sep 17 00:00:00 2001 From: fjf2002 Date: Mon, 10 Jun 2019 21:45:00 +0200 Subject: [PATCH 1/2] MS Azure Authentication added --- docker/db/files/init_db.sql | 2 +- .../frontend/conf/Catalina/localhost/ROOT.xml | 5 ++ kotlin.web.demo.frontend/index.html | 2 + .../src/views/LoginView.kt | 4 +- .../static/css/global-toolbox.css | 4 + .../static/images/azure_icon.svg | 6 ++ kotlin.web.demo.server/build.gradle | 1 + .../webdemo/ApplicationSettings.java | 4 + .../org/jetbrains/webdemo/CommandRunner.java | 6 ++ .../AuthorizationAzureHelper.java | 85 +++++++++++++++++++ .../authorization/AuthorizationHelper.java | 2 + .../webdemo/servlet/AuthorizationServlet.java | 3 + .../main/webapp/META-INF/context.template.xml | 4 + 13 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 kotlin.web.demo.frontend/static/images/azure_icon.svg create mode 100644 kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationAzureHelper.java diff --git a/docker/db/files/init_db.sql b/docker/db/files/init_db.sql index 849762d0d..cf2cfa6df 100644 --- a/docker/db/files/init_db.sql +++ b/docker/db/files/init_db.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS dbinfo ( CREATE TABLE IF NOT EXISTS users ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, client_id VARCHAR(45) NOT NULL, - provider ENUM ('google', 'twitter', 'facebook', 'github', 'jba') NOT NULL, + provider ENUM ('google', 'twitter', 'facebook', 'github', 'jba', 'azure') NOT NULL, username VARCHAR(100) NOT NULL DEFAULT '', CONSTRAINT client_id UNIQUE (client_id, provider) ); diff --git a/docker/frontend/conf/Catalina/localhost/ROOT.xml b/docker/frontend/conf/Catalina/localhost/ROOT.xml index 35dba5768..e802905ca 100644 --- a/docker/frontend/conf/Catalina/localhost/ROOT.xml +++ b/docker/frontend/conf/Catalina/localhost/ROOT.xml @@ -37,4 +37,9 @@ + + + + + diff --git a/kotlin.web.demo.frontend/index.html b/kotlin.web.demo.frontend/index.html index 16cc407cc..9b80a733c 100644 --- a/kotlin.web.demo.frontend/index.html +++ b/kotlin.web.demo.frontend/index.html @@ -97,6 +97,7 @@
+
@@ -244,6 +245,7 @@
+
diff --git a/kotlin.web.demo.frontend/src/views/LoginView.kt b/kotlin.web.demo.frontend/src/views/LoginView.kt index 29549cf37..b0af97a93 100644 --- a/kotlin.web.demo.frontend/src/views/LoginView.kt +++ b/kotlin.web.demo.frontend/src/views/LoginView.kt @@ -19,7 +19,7 @@ package views import org.w3c.dom.HTMLDivElement import org.w3c.dom.events.Event import providers.LoginProvider -import utils.decodeURI +import utils.decodeURIComponent import utils.jquery.jq import kotlin.browser.document import kotlin.js.json @@ -33,7 +33,7 @@ class LoginView(val loginModel: LoginProvider) { jq("#logout").show() isLoggedIn = true - var decodedUserName = decodeURI(userName) + var decodedUserName = decodeURIComponent(userName) decodedUserName = decodedUserName.replace("+", " ") jq("#username").text(decodedUserName) diff --git a/kotlin.web.demo.frontend/static/css/global-toolbox.css b/kotlin.web.demo.frontend/static/css/global-toolbox.css index 26aac04d0..5bd1d6762 100644 --- a/kotlin.web.demo.frontend/static/css/global-toolbox.css +++ b/kotlin.web.demo.frontend/static/css/global-toolbox.css @@ -52,6 +52,10 @@ background: url("/static/images/fb_icon.svg") no-repeat; } +.icon.azure { + background: url("/static/images/azure_icon.svg") no-repeat; +} + .icon.google { background: url("/static/images/icons_all_sprite.svg") no-repeat -200px -46px; } diff --git a/kotlin.web.demo.frontend/static/images/azure_icon.svg b/kotlin.web.demo.frontend/static/images/azure_icon.svg new file mode 100644 index 000000000..30f9a6809 --- /dev/null +++ b/kotlin.web.demo.frontend/static/images/azure_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/kotlin.web.demo.server/build.gradle b/kotlin.web.demo.server/build.gradle index ed1f795c8..870906625 100644 --- a/kotlin.web.demo.server/build.gradle +++ b/kotlin.web.demo.server/build.gradle @@ -13,6 +13,7 @@ apply plugin: 'war' dependencies { compile group: 'org.scribe', name: 'scribe', version: '1.3.7' + compile group: 'com.github.scribejava', name: 'scribejava-apis', version: '6.6.2' compile group: 'org.jetbrains', name: 'annotations', version: '13.0' compile 'org.twitter4j:twitter4j-core:4.0.4' compile 'com.nimbusds:nimbus-jose-jwt:3.9' diff --git a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/ApplicationSettings.java b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/ApplicationSettings.java index 34a0c102f..f16262a96 100644 --- a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/ApplicationSettings.java +++ b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/ApplicationSettings.java @@ -27,6 +27,7 @@ public class ApplicationSettings { public static OauthCredentials FACEBOOK_OAUTH_CREDENTIALS = new OauthCredentials(); public static OauthCredentials TWITTER_OAUTH_CREDENTIALS = new OauthCredentials(); public static OauthCredentials JET_ACCOUNT_CREDENTIALS = new OauthCredentials(); + public static AzureCredentials AZURE_OAUTH_CREDENTIALS = new AzureCredentials(); private ApplicationSettings() { @@ -38,5 +39,8 @@ public static class OauthCredentials { public String SECRET = ""; } + public static class AzureCredentials extends OauthCredentials { + public String TENANT = ""; + } } diff --git a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/CommandRunner.java b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/CommandRunner.java index 60f05a8d9..3587e4d74 100644 --- a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/CommandRunner.java +++ b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/CommandRunner.java @@ -32,6 +32,12 @@ public static void setServerSettingFromTomcatConfig(String setting, String value ApplicationSettings.BACKEND_URL = value; }else if (setting.equals("is_test_version")) { CommonSettings.IS_TEST_VERSION = Boolean.parseBoolean(value); + } else if (setting.equals("azure_key")) { + ApplicationSettings.AZURE_OAUTH_CREDENTIALS.KEY = value; + } else if (setting.equals("azure_secret")) { + ApplicationSettings.AZURE_OAUTH_CREDENTIALS.SECRET = value; + } else if (setting.equals("azure_tenant")) { + ApplicationSettings.AZURE_OAUTH_CREDENTIALS.TENANT = value; } else if (setting.equals("google_key")) { ApplicationSettings.GOOGLE_OAUTH_CREDENTIALS.KEY = value; } else if (setting.equals("google_secret")) { diff --git a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationAzureHelper.java b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationAzureHelper.java new file mode 100644 index 000000000..4edfd3fd3 --- /dev/null +++ b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationAzureHelper.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2019 Franz-Josef Färber + * + * 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 org.jetbrains.webdemo.authorization; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.scribejava.apis.MicrosoftAzureActiveDirectoryApi; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.*; +import com.github.scribejava.core.oauth.OAuth20Service; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.webdemo.ApplicationSettings; +import org.jetbrains.webdemo.ErrorWriter; +import org.jetbrains.webdemo.session.SessionInfo; +import org.jetbrains.webdemo.session.UserInfo; + + +public class AuthorizationAzureHelper extends AuthorizationHelper { + + private static final String PROTECTED_RESOURCE_URL = "https://graph.windows.net/me?api-version=1.6&$select=userPrincipalName,immutableId"; + private static final String TYPE = "azure"; + + private static OAuth20Service azureService; + + public AuthorizationAzureHelper(String host) { + super(host); + } + + @Override + public String getAuthorizationUrl() { + try { + azureService = new ServiceBuilder(ApplicationSettings.AZURE_OAUTH_CREDENTIALS.KEY) + .apiSecret(ApplicationSettings.AZURE_OAUTH_CREDENTIALS.SECRET) + .defaultScope("https://graph.microsoft.com/User.Read") + .callback(getCallbackUrl()) + .build(MicrosoftAzureActiveDirectoryApi.custom(ApplicationSettings.AZURE_OAUTH_CREDENTIALS.TENANT, null)); + return azureService.getAuthorizationUrl(); + } catch (Throwable e) { + ErrorWriter.ERROR_WRITER.writeExceptionToExceptionAnalyzer(e, SessionInfo.TypeOfRequest.AUTHORIZATION.name(), "unknown", TYPE); + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public UserInfo verify(String oauthVerifier) { + try { + final OAuth2AccessToken accessToken = azureService.getAccessToken(oauthVerifier); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + azureService.signRequest(accessToken, request); + + final Response response = azureService.execute(request); + final JsonNode object = new ObjectMapper().readTree(response.getBody()); + + final UserInfo userInfo = new UserInfo(); + userInfo.login(object.get("userPrincipalName").asText(), object.get("immutableId").asText(), TYPE); + return userInfo; + } catch (Throwable e) { + ErrorWriter.ERROR_WRITER.writeExceptionToExceptionAnalyzer(e, SessionInfo.TypeOfRequest.AUTHORIZATION.name(), "unknown", "azure: " + oauthVerifier); + throw new RuntimeException(e); + } + } + + @NotNull + @Override + protected String getType() { + return TYPE; + } +} diff --git a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationHelper.java b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationHelper.java index f1099c6a8..df73c4f38 100644 --- a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationHelper.java +++ b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/authorization/AuthorizationHelper.java @@ -35,6 +35,8 @@ public AuthorizationHelper(String host) { public static AuthorizationHelper getHelper(String type, String host) { switch (type) { + case "azure": + return new AuthorizationAzureHelper(host); case "twitter": return new AuthorizationTwitterHelper(host); case "google": diff --git a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/servlet/AuthorizationServlet.java b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/servlet/AuthorizationServlet.java index f2f878e3f..4231ec9ba 100644 --- a/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/servlet/AuthorizationServlet.java +++ b/kotlin.web.demo.server/src/main/java/org/jetbrains/webdemo/servlet/AuthorizationServlet.java @@ -57,6 +57,9 @@ public void init() { try { InitialContext initialContext = new InitialContext(); Context envCtx = (Context) initialContext.lookup("java:comp/env"); + CommandRunner.setServerSettingFromTomcatConfig("azure_key", (String) envCtx.lookup("azure_key")); + CommandRunner.setServerSettingFromTomcatConfig("azure_secret", (String) envCtx.lookup("azure_secret")); + CommandRunner.setServerSettingFromTomcatConfig("azure_tenant", (String) envCtx.lookup("azure_tenant")); CommandRunner.setServerSettingFromTomcatConfig("google_key", (String) envCtx.lookup("google_key")); CommandRunner.setServerSettingFromTomcatConfig("google_secret", (String) envCtx.lookup("google_secret")); CommandRunner.setServerSettingFromTomcatConfig("twitter_key", (String) envCtx.lookup("twitter_key")); diff --git a/kotlin.web.demo.server/src/main/webapp/META-INF/context.template.xml b/kotlin.web.demo.server/src/main/webapp/META-INF/context.template.xml index 305759106..df458c4bf 100644 --- a/kotlin.web.demo.server/src/main/webapp/META-INF/context.template.xml +++ b/kotlin.web.demo.server/src/main/webapp/META-INF/context.template.xml @@ -17,6 +17,10 @@ + + + + Date: Mon, 10 Jun 2019 21:53:31 +0200 Subject: [PATCH 2/2] MS Azure Authentication added --- kotlin.web.demo.frontend/src/utils/common.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlin.web.demo.frontend/src/utils/common.kt b/kotlin.web.demo.frontend/src/utils/common.kt index fa4447d42..29d85c301 100644 --- a/kotlin.web.demo.frontend/src/utils/common.kt +++ b/kotlin.web.demo.frontend/src/utils/common.kt @@ -154,6 +154,6 @@ inline fun Window.eval(code: String): dynamic = asDynamic().eval(code) inline fun Window.getSelection(): dynamic = asDynamic().getSelection() -external fun decodeURI(uri: String): String = definedExternally +external fun decodeURIComponent(uri: String): String = definedExternally external fun encodeURIComponent(component: String): String = definedExternally \ No newline at end of file