This is an automated email from the ASF dual-hosted git repository. sijie pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/pulsar-manager.git
The following commit(s) were added to refs/heads/master by this push: new 7138028 Support third party login for example GitHub (#222) 7138028 is described below commit 713802827eb00214f295195de16f1c82351d0499 Author: tuteng <guangn...@apache.org> AuthorDate: Thu Dec 5 03:04:26 2019 +0800 Support third party login for example GitHub (#222) Fix #14 ### Motivation This pr is used to support Github authentication. Next, I will add support for user authorization. ### Modifications * Support authentication by Github * Add user table * Add a third-party login page ### Verifying this change Add unit test --- build.gradle | 1 + front-end/src/api/socialsignin.js | 23 ++++ front-end/src/icons/svg/github.svg | 1 + front-end/src/views/login/index.vue | 19 ++- front-end/src/views/login/socialsignin.vue | 44 ++++--- src/README.md | 25 ++++ .../ThirdPartyLoginCallbackController.java | 113 ++++++++++++++++++ .../pulsar/manager/dao/UsersRepositoryImpl.java | 62 ++++++++++ .../pulsar/manager/entity/GithubAuthEntity.java | 36 ++++++ .../manager/entity/GithubUserInfoEntity.java | 108 +++++++++++++++++ .../pulsar/manager/entity/UserInfoEntity.java | 38 ++++++ .../pulsar/manager/entity/UsersRepository.java | 58 +++++++++ .../manager/interceptor/WebAppConfigurer.java | 4 +- .../apache/pulsar/manager/mapper/UsersMapper.java | 54 +++++++++ .../manager/service/ThirdPartyLoginService.java | 37 ++++++ .../service/impl/GithubLoginServiceImpl.java | 112 ++++++++++++++++++ .../org/apache/pulsar/manager/utils/HttpUtil.java | 19 ++- src/main/resources/META-INF/sql/herddb-schema.sql | 12 ++ src/main/resources/META-INF/sql/mysql-schema.sql | 13 +++ .../resources/META-INF/sql/postgresql-schema.sql | 13 +++ src/main/resources/META-INF/sql/sqlite-schema.sql | 12 ++ src/main/resources/application.properties | 24 +++- src/main/resources/templates/index.html | 40 +++++++ .../manager/dao/UsersRepositoryImplTest.java | 100 ++++++++++++++++ .../service/GithubLoginServiceImplTest.java | 129 +++++++++++++++++++++ 25 files changed, 1069 insertions(+), 28 deletions(-) diff --git a/build.gradle b/build.gradle index 8c6803e..960354d 100644 --- a/build.gradle +++ b/build.gradle @@ -97,6 +97,7 @@ dependencies { compile group: 'org.springframework.boot', name: 'spring-boot-devtools', version: springBootVersion compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-zuul', version: springBootVersion compile group: 'org.mybatis.spring.boot', name: 'mybatis-spring-boot-starter', version: springMybatisVersion + compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf', version: springBootVersion compile group: 'org.postgresql', name: 'postgresql', version: postgresqlVersion compile group: 'org.herddb', name: 'herddb-jdbc', version: herddbVersion compile group: 'javax.validation', name: 'validation-api', version: javaxValidationVersion diff --git a/front-end/src/api/socialsignin.js b/front-end/src/api/socialsignin.js new file mode 100644 index 0000000..fee4336 --- /dev/null +++ b/front-end/src/api/socialsignin.js @@ -0,0 +1,23 @@ +/* + * 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. + */ +import request from '@/utils/request' + +const BASE_URL = '/pulsar-manager/third-party-login' + +export function getGithubLoginHost() { + return request({ + url: BASE_URL + `/github/login`, + method: 'get' + }) +} diff --git a/front-end/src/icons/svg/github.svg b/front-end/src/icons/svg/github.svg new file mode 100644 index 0000000..3899712 --- /dev/null +++ b/front-end/src/icons/svg/github.svg @@ -0,0 +1 @@ +<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub icon</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3 [...] \ No newline at end of file diff --git a/front-end/src/views/login/index.vue b/front-end/src/views/login/index.vue index 758714f..3d95039 100644 --- a/front-end/src/views/login/index.vue +++ b/front-end/src/views/login/index.vue @@ -53,6 +53,9 @@ </el-form-item> <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">{{ $t('login.logIn') }}</el-button> + <el-button class="thirdparty-button" type="primary" @click="showDialog=true"> + Or connect with + </el-button> </el-form> <el-dialog :title="$t('login.thirdparty')" :visible.sync="showDialog" append-to-body> @@ -119,6 +122,9 @@ export default { destroyed() { // window.removeEventListener('hashchange', this.afterQRScan) }, + mounted() { + window.addEventListener('message', this.handleMessage) + }, methods: { showPwd() { if (this.passwordType === 'password') { @@ -127,6 +133,12 @@ export default { this.passwordType = 'password' } }, + handleMessage(event) { + const data = event.data + if (data.hasOwnProperty('name') && data.hasOwnProperty('accessToken')) { + // to do set token, track task https://github.com/apache/pulsar-manager/issues/14 + } + }, handleLogin() { this.$refs.loginForm.validate(valid => { if (valid) { @@ -276,7 +288,12 @@ $light_gray:#eee; .thirdparty-button { position: absolute; right: 35px; - bottom: 28px; + bottom: 1px; + } + @media only screen and (max-width: 470px) { + .thirdparty-button { + display: none; + } } } </style> diff --git a/front-end/src/views/login/socialsignin.vue b/front-end/src/views/login/socialsignin.vue index d9bb8d7..51d1735 100644 --- a/front-end/src/views/login/socialsignin.vue +++ b/front-end/src/views/login/socialsignin.vue @@ -15,36 +15,33 @@ --> <template> <div class="social-signup-container"> - <div class="sign-btn" @click="wechatHandleClick('wechat')"> - <span class="wx-svg-container"><svg-icon icon-class="wechat" class="icon"/></span> 微信 - </div> - <div class="sign-btn" @click="tencentHandleClick('tencent')"> - <span class="qq-svg-container"><svg-icon icon-class="qq" class="icon"/></span> QQ + <div class="sign-btn" @click="githubHandleClick('github')"> + <span class="github-container"><svg-icon icon-class="github" class="icon"/></span> GitHub </div> </div> </template> <script> -// import openWindow from '@/utils/openWindow' +import openWindow from '@/utils/openWindow' +import { getGithubLoginHost } from '@/api/socialsignin' export default { name: 'SocialSignin', methods: { - wechatHandleClick(thirdpart) { - alert('ok') - // this.$store.commit('SET_AUTH_TYPE', thirdpart) - // const appid = 'xxxxx' - // const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect') - // const url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + appid + '&redirect_uri=' + redirect_uri + '&response_type=code&scope=snsapi_login#wechat_redirect' - // openWindow(url, thirdpart, 540, 540) - }, - tencentHandleClick(thirdpart) { - alert('ok') - // this.$store.commit('SET_AUTH_TYPE', thirdpart) - // const client_id = 'xxxxx' - // const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect') - // const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri - // openWindow(url, thirdpart, 540, 540) + githubHandleClick(thirdpart) { + getGithubLoginHost().then(response => { + if (!response.data) return + if (response.data.message === 'success') { + openWindow(decodeURIComponent(response.data.url), thirdpart, 540, 540) + } else { + this.$notify({ + title: 'failed', + message: response.data.message, + type: 'error', + duration: 3000 + }) + } + }) } } } @@ -62,8 +59,7 @@ export default { font-size: 24px; margin-top: 8px; } - .wx-svg-container, - .qq-svg-container { + .github-container { display: inline-block; width: 40px; height: 40px; @@ -74,7 +70,7 @@ export default { margin-bottom: 20px; margin-right: 5px; } - .wx-svg-container { + .github-container { background-color: #8ada53; } .qq-svg-container { diff --git a/src/README.md b/src/README.md index b18eaec..858f52d 100644 --- a/src/README.md +++ b/src/README.md @@ -101,3 +101,28 @@ export SECRET_KEY="file:///secret-key-path" docker run -it -p 9527:9527 -e REDIRECT_HOST=http://192.168.55.182 -e REDIRECT_PORT=9527 -e DRIVER_CLASS_NAME=org.postgresql.Driver -e URL='jdbc:postgresql://127.0.0.1:5432/pulsar_manager' -e USERNAME=pulsar -e PASSWORD=pulsar -e LOG_LEVEL=DEBUG -e JWT_TOKEN=$JWT_TOKEN -e PRIVATE_KEY=$PRIVATE_KEY -e PUBLIC_KEY=$PUBLIC_KEY -v $PWD:/data -v $PWD/secret-key-path:/pulsar-manager/secret-key-path apachepulsar/pulsar-manager:v0.1.0 /bin/sh ``` +### Enable Github Login + +#### Third party login options + +``` +# default empty, current options github +third.party.login.option= +``` + +#### Github login configuration + +``` +# The client ID you received from GitHub when you registered https://github.com/settings/applications/new. +github.client.id=your-client-id +# The client secret you received from GitHub for your GitHub App. +github.client.secret=your-client-secret +github.oauth.host=https://github.com/login/oauth/access_token +github.user.info=https://api.github.com/user +github.login.host=https://github.com/login/oauth/authorize +github.redirect.host=http://localhost:9527 + +# Expiration time of token for third party platform, unit second. +# 60 * 60 * 24 * 7 +user.access.token.expire=604800 +``` diff --git a/src/main/java/org/apache/pulsar/manager/controller/ThirdPartyLoginCallbackController.java b/src/main/java/org/apache/pulsar/manager/controller/ThirdPartyLoginCallbackController.java new file mode 100644 index 0000000..f9f175d --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/controller/ThirdPartyLoginCallbackController.java @@ -0,0 +1,113 @@ +/** + * 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.apache.pulsar.manager.controller; + +import com.google.common.collect.Maps; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.manager.entity.UserInfoEntity; +import org.apache.pulsar.manager.service.ThirdPartyLoginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +/** + * Callback function of third party platform login. + */ +@Slf4j +@Controller +@RequestMapping(value = "/pulsar-manager/third-party-login") +@Api(description = "Calling the request below this class does not require authentication because " + + "the user has not logged in yet.") +@Validated +public class ThirdPartyLoginCallbackController { + + @Value("${github.client.id}") + private String githubClientId; + + @Value("${github.login.host}") + private String githubLoginHost; + + @Value("${github.redirect.host}") + private String githubRedirectHost; + + @Autowired + private ThirdPartyLoginService thirdPartyLoginService; + + @ApiOperation(value = "When use pass github authentication, Github platform will carry code parameter to call " + + "back this address actively. At this time, we can request token and get user information through " + + "this code." + + "Reference document: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/") + @ApiResponses({ + @ApiResponse(code = 200, message = "ok"), + @ApiResponse(code = 404, message = "Not found"), + @ApiResponse(code = 500, message = "Internal server error") + }) + @RequestMapping(value = "/callback/github") + public String githubCallbackIndex(Model model, @RequestParam() String code) { + Map<String, String> parameters = Maps.newHashMap(); + parameters.put("code", code); + String accessToken = thirdPartyLoginService.getAuthToken(parameters); + Map<String, String> authenticationMap = Maps.newHashMap(); + authenticationMap.put("access_token", accessToken); + UserInfoEntity userInfoEntity = thirdPartyLoginService.getUserInfo(authenticationMap); + if (userInfoEntity == null) { + model.addAttribute("messages", "Authentication failed, please check carefully"); + model.addAttribute("flag", false); + return "index"; + } + model.addAttribute("message", "Authentication successful, logging in"); + model.addAttribute("flag", true); + model.addAttribute("userInfo", userInfoEntity); + return "index"; + } + + @ApiOperation(value = "Github's third-party authorized login address, HTTP GET request, needs to carry " + + "client_id and redirect_host parameters. Parameter client_id and redirect_host needs to be applied " + + "from github platform https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/.") + @ApiResponses({ + @ApiResponse(code = 200, message = "ok"), + @ApiResponse(code = 404, message = "Not found"), + @ApiResponse(code = 500, message = "Internal server error") + }) + @RequestMapping(value = "/github/login", method = RequestMethod.GET) + public @ResponseBody ResponseEntity<Map<String, Object>> getGithubLoginUrl() { + Map<String, Object> result = Maps.newHashMap(); + String url = githubLoginHost + "?client_id=" + githubClientId + + "&redirect_host=" + githubRedirectHost + "/pulsar-manager/third-party-login/callback/github"; + try { + result.put("url", URLEncoder.encode(url, StandardCharsets.UTF_8.toString())); + result.put("message", "success"); + } catch (UnsupportedEncodingException e) { + log.error("Url encoding failed, please check: [{}]", url); + result.put("message", "Url encoding failed, please check:: " + url); + } + return ResponseEntity.ok(result); + } +} diff --git a/src/main/java/org/apache/pulsar/manager/dao/UsersRepositoryImpl.java b/src/main/java/org/apache/pulsar/manager/dao/UsersRepositoryImpl.java new file mode 100644 index 0000000..410c5f1 --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/dao/UsersRepositoryImpl.java @@ -0,0 +1,62 @@ +/** + * 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.apache.pulsar.manager.dao; + +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import org.apache.pulsar.manager.entity.UserInfoEntity; +import org.apache.pulsar.manager.entity.UsersRepository; +import org.apache.pulsar.manager.mapper.UsersMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public class UsersRepositoryImpl implements UsersRepository { + + private final UsersMapper usersMapper; + + @Autowired + public UsersRepositoryImpl(UsersMapper usersMapper) { + this.usersMapper = usersMapper; + } + + @Override + public long save(UserInfoEntity userInfoEntity) { + long userId = this.usersMapper.save(userInfoEntity); + return userId; + } + + @Override + public Optional<UserInfoEntity> findByUserName(String name) { + return Optional.ofNullable(this.usersMapper.findByUserName(name)); + } + + @Override + public Page<UserInfoEntity> findUsersList(Integer pageNum, Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + return this.usersMapper.findUsersList(); + } + + @Override + public void update(UserInfoEntity userInfoEntity) { + this.usersMapper.update(userInfoEntity); + } + + @Override + public void delete(String name) { + this.usersMapper.delete(name); + } +} diff --git a/src/main/java/org/apache/pulsar/manager/entity/GithubAuthEntity.java b/src/main/java/org/apache/pulsar/manager/entity/GithubAuthEntity.java new file mode 100644 index 0000000..986ce05 --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/entity/GithubAuthEntity.java @@ -0,0 +1,36 @@ +/** + * 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.apache.pulsar.manager.entity; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Github auth information entity. + */ +@Getter +@Setter +@NoArgsConstructor +public class GithubAuthEntity { + + @SerializedName("access_token") + private String accessToken; + + @SerializedName("token_type") + private String token_type; + + private String scope; +} diff --git a/src/main/java/org/apache/pulsar/manager/entity/GithubUserInfoEntity.java b/src/main/java/org/apache/pulsar/manager/entity/GithubUserInfoEntity.java new file mode 100644 index 0000000..0409454 --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/entity/GithubUserInfoEntity.java @@ -0,0 +1,108 @@ +/** + * 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.apache.pulsar.manager.entity; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Github user information entity. + */ +@Getter +@Setter +@NoArgsConstructor +@Data +public class GithubUserInfoEntity { + private String login; + private Long id; + + @SerializedName("node_id") + private String nodeId; + + @SerializedName("avatar_url") + private String avatarUrl; + + @SerializedName("gravatar_id") + private String gravatarId; + + private String url; + + @SerializedName("html_url") + private String htmlUrl; + + @SerializedName("followers_url") + private String followersUrl; + + @SerializedName("following_url") + private String followingUrl; + + @SerializedName("gists_url") + private String gistsUrl; + + @SerializedName("starred_url") + private String starredUrl; + + @SerializedName("subscriptions_url") + private String subscriptionsUrl; + + @SerializedName("organizations_url") + private String organizationsUrl; + + @SerializedName("repos_url") + private String reposUrl; + + @SerializedName("events_url") + private String eventsUrl; + + @SerializedName("received_events_url") + private String receivedEventsUrl; + + private String type; + + @SerializedName("site_admin") + private String siteAdmin; + + private String name; + + private String company; + + private String blog; + + private String location; + + private String email; + + private String hireable; + + private String bio; + + @SerializedName("public_repos") + private Integer publicRepos; + + @SerializedName("public_gists") + private Integer publicGists; + + private Long followers; + + private Long following; + + @SerializedName("created_at") + private String createdAt; + + @SerializedName("updated_at") + private String updatedAt; +} diff --git a/src/main/java/org/apache/pulsar/manager/entity/UserInfoEntity.java b/src/main/java/org/apache/pulsar/manager/entity/UserInfoEntity.java new file mode 100644 index 0000000..bff8d54 --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/entity/UserInfoEntity.java @@ -0,0 +1,38 @@ +/** + * 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.apache.pulsar.manager.entity; + +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Pulsar Manager platform entity. + */ +@Getter +@Setter +@NoArgsConstructor +@Data +public class UserInfoEntity { + private long userId; + private String name; + private String description; + private String location; + private String company; + private String phoneNumber; + private String email; + private String accessToken; + private long expire; +} diff --git a/src/main/java/org/apache/pulsar/manager/entity/UsersRepository.java b/src/main/java/org/apache/pulsar/manager/entity/UsersRepository.java new file mode 100644 index 0000000..fa467bb --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/entity/UsersRepository.java @@ -0,0 +1,58 @@ +/** + * 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.apache.pulsar.manager.entity; + +import com.github.pagehelper.Page; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UsersRepository { + + /** + * Create a user. + * @param userInfoEntity + * @return user id + */ + long save(UserInfoEntity userInfoEntity); + + /** + * Get a user information by user name. + * @param name The user name + * @return UserInfoEntity + */ + Optional<UserInfoEntity> findByUserName(String name); + + + /** + * Get user list, support paging. + * @param pageNum Get the data on which page. + * @param pageSize The number of data per page + * @return A list of UserInfoEntity. + */ + Page<UserInfoEntity> findUsersList(Integer pageNum, Integer pageSize); + + /** + * Update a user information. + * @param userInfoEntity UserInfoEntity + */ + void update(UserInfoEntity userInfoEntity); + + /** + * Delete a user by username. + * @param name username + */ + void delete(String name); +} diff --git a/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java b/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java index 818e294..c79f4b8 100644 --- a/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java +++ b/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java @@ -27,6 +27,8 @@ public class WebAppConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(adminHandlerInterceptor).addPathPatterns("/**").excludePathPatterns("/pulsar-manager/login"); + registry.addInterceptor(adminHandlerInterceptor).addPathPatterns("/**") + .excludePathPatterns("/pulsar-manager/login") + .excludePathPatterns("/pulsar-manager/third-party-login/**"); } } diff --git a/src/main/java/org/apache/pulsar/manager/mapper/UsersMapper.java b/src/main/java/org/apache/pulsar/manager/mapper/UsersMapper.java new file mode 100644 index 0000000..d792c12 --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/mapper/UsersMapper.java @@ -0,0 +1,54 @@ +/** + * 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.apache.pulsar.manager.mapper; + +import com.github.pagehelper.Page; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import org.apache.pulsar.manager.entity.UserInfoEntity; + +@Mapper +public interface UsersMapper { + + @Insert("INSERT INTO users (access_token, name, description, email, phone_number" + + ", location, company, expire)" + + "VALUES (#{accessToken}, #{name}, #{description}, #{email}, #{phoneNumber}" + + ", #{location}, #{company}, #{expire})") + @Options(useGeneratedKeys=true, keyProperty="userId", keyColumn="user_id") + long save(UserInfoEntity userInfoEntity); + + @Select("SELECT access_token AS accessToken, user_id AS userId, name, description, email," + + "phone_number AS phoneNumber, location, company, expire " + + "FROM users " + + "WHERE name = #{name}") + UserInfoEntity findByUserName(String name); + + @Select("SELECT access_token AS accessToken, user_id AS userId, name, description, email," + + "phone_number AS phoneNumber, location, company, expire " + + "FROM users") + Page<UserInfoEntity> findUsersList(); + + @Update("UPDATE users " + + "SET access_token = #{accessToken}, description = #{description}, email = #{email}," + + "phone_number = #{phoneNumber}, location = #{location}, company = #{company}, expire=#{expire} " + + "WHERE name = #{name}") + void update(UserInfoEntity userInfoEntity); + + @Delete("DELETE FROM users WHERE name=#{name}") + void delete(String name); +} diff --git a/src/main/java/org/apache/pulsar/manager/service/ThirdPartyLoginService.java b/src/main/java/org/apache/pulsar/manager/service/ThirdPartyLoginService.java new file mode 100644 index 0000000..9f44bd7 --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/service/ThirdPartyLoginService.java @@ -0,0 +1,37 @@ +/** + * 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.apache.pulsar.manager.service; + +import org.apache.pulsar.manager.entity.UserInfoEntity; + +import java.util.Map; + +public interface ThirdPartyLoginService { + + /** + * Obtaining an authentication token from a third-party platform. + * @param parameters For a kv type map, different third-party platforms may need to pass different parameters, + * which are passed according to the actual situation and analyzed in their implementation classes. + * @return String format access token information + */ + String getAuthToken(Map<String, String> parameters); + + /** + * Acquiring user information according to an authentication token. + * @param authenticationMap For a kv type map, different third-party platforms need different parameters, + * which are passed through the map structure. + * @return UserInfoEntity + */ + UserInfoEntity getUserInfo(Map<String, String> authenticationMap); +} diff --git a/src/main/java/org/apache/pulsar/manager/service/impl/GithubLoginServiceImpl.java b/src/main/java/org/apache/pulsar/manager/service/impl/GithubLoginServiceImpl.java new file mode 100644 index 0000000..4a2eacb --- /dev/null +++ b/src/main/java/org/apache/pulsar/manager/service/impl/GithubLoginServiceImpl.java @@ -0,0 +1,112 @@ +/** + * 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.apache.pulsar.manager.service.impl; + +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.manager.entity.GithubAuthEntity; +import org.apache.pulsar.manager.entity.GithubUserInfoEntity; +import org.apache.pulsar.manager.entity.UserInfoEntity; +import org.apache.pulsar.manager.service.ThirdPartyLoginService; +import org.apache.pulsar.manager.utils.HttpUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.UnsupportedEncodingException; +import java.util.Map; + +/** + * Github login for get user information. + */ +@Slf4j +@Service +public class GithubLoginServiceImpl implements ThirdPartyLoginService { + + @Value("${github.client.id}") + private String githubClientId; + + @Value("${github.client.secret}") + private String githubClientSecret; + + @Value("${github.oauth.host}") + private String githubAuthHost; + + @Value("${github.user.info}") + private String githubUserInfo; + + /** + * Get user access token from github. + * @param parameters For get code to github. + * GitHub redirects back to local site with a temporary code in a code parameter as well as the state + * you provided in the previous step in a state parameter.The temporary code will expire after 10 minutes. + * @return access_token of string type + */ + public String getAuthToken(Map<String, String> parameters) { + Map<String, String> header = Maps.newHashMap(); + header.put("Content-Type", "application/json"); + header.put("Accept", "application/json"); + Map<String, Object> body = Maps.newHashMap(); + body.put("client_id", githubClientId); + body.put("client_secret", githubClientSecret); + if (!parameters.containsKey("code")) { + log.error("Parameter does not contain code field, which is illegal."); + return null; + } + body.put("code", parameters.get("code")); + Gson gson = new Gson(); + try { + // result example: access_token=your-token&token_type=bearer + String result = HttpUtil.doPost(githubAuthHost, header, gson.toJson(body)); + GithubAuthEntity githubAuthEntity = gson.fromJson(result, GithubAuthEntity.class); + log.info("Success get access token from github"); + return githubAuthEntity.getAccessToken(); + } catch (UnsupportedEncodingException e) { + log.error("Failed get access token from github, error stack: {}", e.getCause()); + return null; + } + } + + /** + * Get user information from github by access token. + * @param authenticationMap Authentication mark requesting user information. + * @return UserInfoEntity + */ + public UserInfoEntity getUserInfo(Map<String, String> authenticationMap) { + Map<String, String> header = Maps.newHashMap(); + header.put("Content-Type", "application/json"); + if (!authenticationMap.containsKey("access_token")) { + log.error("The authenticationMap does not contain access_token field, which is illegal."); + return null; + } + header.put("Authorization", "token " + authenticationMap.get("access_token")); + String result = HttpUtil.doGet(githubUserInfo, header); + Gson gson = new Gson(); + GithubUserInfoEntity githubUserInfoEntity = gson.fromJson(result, GithubUserInfoEntity.class); + if (githubUserInfoEntity == null) { + log.error("Get user information from github failed."); + return null; + } + UserInfoEntity userInfoEntity = new UserInfoEntity(); + userInfoEntity.setCompany(githubUserInfoEntity.getCompany()); + // User 'login' field of github as platform name, because name field of github often empty. + // Github's name field is more like an alias. + userInfoEntity.setName(githubUserInfoEntity.getLogin()); + userInfoEntity.setDescription(githubUserInfoEntity.getBio()); + userInfoEntity.setEmail(githubUserInfoEntity.getEmail()); + userInfoEntity.setLocation(githubUserInfoEntity.getLocation()); + userInfoEntity.setAccessToken(authenticationMap.get("access_token")); + return userInfoEntity; + } +} diff --git a/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java b/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java index f08b7d4..cf0ba45 100644 --- a/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java +++ b/src/main/java/org/apache/pulsar/manager/utils/HttpUtil.java @@ -17,6 +17,7 @@ import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; 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.client.methods.HttpUriRequest; import org.apache.http.entity.StringEntity; @@ -59,7 +60,23 @@ public class HttpUtil { return httpRequest(request, header); } - public static String doPut(String url, Map<String, String> header, String body) throws UnsupportedEncodingException { + /** + * HTTP post method. + * @param url Destination host + * @param header Header information + * @param body Body information + * @return HTTP response information + * @throws UnsupportedEncodingException + */ + public static String doPost(String url, Map<String, String> header, String body) + throws UnsupportedEncodingException { + HttpPost request = new HttpPost(url); + request.setEntity(new StringEntity(body)); + return httpRequest(request, header); + } + + public static String doPut(String url, Map<String, String> header, String body) + throws UnsupportedEncodingException { HttpPut request = new HttpPut(url); request.setEntity(new StringEntity(body)); return httpRequest(request, header); diff --git a/src/main/resources/META-INF/sql/herddb-schema.sql b/src/main/resources/META-INF/sql/herddb-schema.sql index 61d7469..28fc09a 100644 --- a/src/main/resources/META-INF/sql/herddb-schema.sql +++ b/src/main/resources/META-INF/sql/herddb-schema.sql @@ -113,3 +113,15 @@ CREATE TABLE IF NOT EXISTS tokens ( description varchar(128), token varchar(1024) ); + +CREATE TABLE IF NOT EXISTS users ( + user_id BIGINT PRIMARY KEY AUTO_INCREMENT, + access_token varchar(256) NOT NULL, + name varchar(256) NOT NULL, + description varchar(128), + email varchar(256), + phone_number varchar(48), + location varchar(256), + company varchar(256), + expire LONG NOT NULL +); diff --git a/src/main/resources/META-INF/sql/mysql-schema.sql b/src/main/resources/META-INF/sql/mysql-schema.sql index ba22fbb..4607a31 100644 --- a/src/main/resources/META-INF/sql/mysql-schema.sql +++ b/src/main/resources/META-INF/sql/mysql-schema.sql @@ -122,4 +122,17 @@ CREATE TABLE IF NOT EXISTS tokens ( description varchar(128), token varchar(1024), UNIQUE (role) +)ENGINE=InnoDB CHARACTER SET utf8; + +CREATE TABLE IF NOT EXISTS users ( + user_id BIGINT PRIMARY KEY AUTO_INCREMENT, + access_token varchar(256) NOT NULL, + name varchar(256) NOT NULL, + description varchar(128), + email varchar(256), + phone_number varchar(48), + location varchar(256), + company varchar(256), + expire LONG NOT NULL, + UNIQUE (name) )ENGINE=InnoDB CHARACTER SET utf8; \ No newline at end of file diff --git a/src/main/resources/META-INF/sql/postgresql-schema.sql b/src/main/resources/META-INF/sql/postgresql-schema.sql index 4f4ccf4..bf55254 100644 --- a/src/main/resources/META-INF/sql/postgresql-schema.sql +++ b/src/main/resources/META-INF/sql/postgresql-schema.sql @@ -122,4 +122,17 @@ CREATE TABLE IF NOT EXISTS tokens ( description varchar(128), token varchar(1024) NOT NUll, UNIQUE (role) +); + +CREATE TABLE IF NOT EXISTS users ( + user_id BIGSERIAL PRIMARY KEY AUTO_INCREMENT, + access_token varchar(256) NOT NULL, + name varchar(256) NOT NULL, + description varchar(128), + email varchar(256), + phone_number varchar(48), + location varchar(256), + company varchar(256), + expire BIGINT NOT NULL, + UNIQUE (name) ); \ No newline at end of file diff --git a/src/main/resources/META-INF/sql/sqlite-schema.sql b/src/main/resources/META-INF/sql/sqlite-schema.sql index 7e6aa09..4ae82be 100644 --- a/src/main/resources/META-INF/sql/sqlite-schema.sql +++ b/src/main/resources/META-INF/sql/sqlite-schema.sql @@ -120,4 +120,16 @@ CREATE TABLE IF NOT EXISTS tokens ( UNIQUE (role) ); +CREATE TABLE IF NOT EXISTS users ( + user_id integer PRIMARY KEY AUTOINCREMENT, + access_token varchar(256) NOT NULL, + name varchar(256) NOT NULL, + description varchar(128), + email varchar(256), + phone_number varchar(48), + location varchar(256), + company varchar(256), + expire integer NOT NUll, + UNIQUE (name) +); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index dbace83..9552e75 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -20,7 +20,7 @@ logging.path= logging.file=pulsar-manager.log # DEBUG print execute sql -logging.level.org.apache=DEBUG +logging.level.org.apache=INFO mybatis.type-aliases-package=org.apache.pulsar.manager @@ -105,3 +105,25 @@ init.delay.interval=0 # cluster data reload cluster.cache.reload.interval.ms=60000 + +# Third party login options +third.party.login.option= + +# Github login configuration +github.client.id=your-client-id +github.client.secret=your-client-secret +github.oauth.host=https://github.com/login/oauth/access_token +github.user.info=https://api.github.com/user +github.login.host=https://github.com/login/oauth/authorize +github.redirect.host=http://localhost:9527 + +user.access.token.expire=604800 + +# thymeleaf configuration for third login. +spring.thymeleaf.cache=false +spring.thymeleaf.prefix=classpath:/templates/ +spring.thymeleaf.check-template-location=true +spring.thymeleaf.suffix=.html +spring.thymeleaf.encoding=UTF-8 +spring.thymeleaf.servlet.content-type=text/html +spring.thymeleaf.mode=HTML5 diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..9cd4011 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,40 @@ +<!-- + + 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. + +--> +<!DOCTYPE html> +<html lang="en" xmlns:th="http://www.thymeleaf.org"> +<head> + <meta charset="utf-8"> + <title>Pulsar Manager</title> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> +</head> +<body> +<div> + <p th:utext="${message}"></p> +</div> +</body> +<script th:inline="javascript"> + window.onload = function() { + var flag = [[${flag}]] + if (flag) { + var userInfo = [[${userInfo}]] + var targetOrigin = window.opener; + targetOrigin.postMessage(userInfo, 'http://' + window.location.host); + window.close(); + } + } +</script> +</html> \ No newline at end of file diff --git a/src/test/java/org/apache/pulsar/manager/dao/UsersRepositoryImplTest.java b/src/test/java/org/apache/pulsar/manager/dao/UsersRepositoryImplTest.java new file mode 100644 index 0000000..ac08a7a --- /dev/null +++ b/src/test/java/org/apache/pulsar/manager/dao/UsersRepositoryImplTest.java @@ -0,0 +1,100 @@ +/** + * 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.apache.pulsar.manager.dao; + +import com.github.pagehelper.Page; +import org.apache.pulsar.manager.PulsarManagerApplication; +import org.apache.pulsar.manager.entity.UserInfoEntity; +import org.apache.pulsar.manager.entity.UsersRepository; +import org.apache.pulsar.manager.profiles.HerdDBTestProfile; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Optional; + +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = { + PulsarManagerApplication.class, + HerdDBTestProfile.class + } +) +@ActiveProfiles("test") +public class UsersRepositoryImplTest { + + @Autowired + private UsersRepository usersRepository; + + private void initUser(UserInfoEntity userInfoEntity) { + userInfoEntity.setAccessToken("test-access-token"); + userInfoEntity.setEmail("t...@apache.org"); + userInfoEntity.setLocation("bj"); + userInfoEntity.setDescription("test-description"); + userInfoEntity.setName("test-user"); + userInfoEntity.setExpire(157900045678l); + userInfoEntity.setPhoneNumber("1356789023456"); + } + + private void validateUser(UserInfoEntity user) { + Assert.assertEquals(user.getName(), "test-user"); + Assert.assertEquals(user.getExpire(), 157900045678l); + Assert.assertEquals(user.getPhoneNumber(), "1356789023456"); + Assert.assertEquals(user.getDescription(), "test-description"); + Assert.assertEquals(user.getLocation(), "bj"); + Assert.assertEquals(user.getEmail(), "t...@apache.org"); + Assert.assertEquals(user.getAccessToken(), "test-access-token"); + } + + @Test + public void getUsersListTest() { + UserInfoEntity userInfoEntity = new UserInfoEntity(); + initUser(userInfoEntity); + + usersRepository.save(userInfoEntity); + + Page<UserInfoEntity> userInfoEntities = usersRepository.findUsersList(1, 10); + userInfoEntities.count(true); + userInfoEntities.getResult().forEach((user) -> { + validateUser(user); + usersRepository.delete(user.getName()); + }); + } + + @Test + public void getAndUpdateUserTest() { + UserInfoEntity userInfoEntity = new UserInfoEntity(); + initUser(userInfoEntity); + usersRepository.save(userInfoEntity); + Optional<UserInfoEntity> userInfoEntityOptional = usersRepository.findByUserName(userInfoEntity.getName()); + UserInfoEntity getUserInfoEntity = userInfoEntityOptional.get(); + validateUser(getUserInfoEntity); + userInfoEntity.setPhoneNumber("1356789023456"); + userInfoEntity.setEmail("te...@apache.org"); + usersRepository.update(userInfoEntity); + + userInfoEntityOptional = usersRepository.findByUserName(userInfoEntity.getName()); + UserInfoEntity updateUserInfoEntity = userInfoEntityOptional.get(); + Assert.assertEquals(updateUserInfoEntity.getPhoneNumber(), "1356789023456"); + Assert.assertEquals(updateUserInfoEntity.getEmail(), "te...@apache.org"); + + usersRepository.delete(updateUserInfoEntity.getName()); + userInfoEntityOptional = usersRepository.findByUserName(userInfoEntity.getName()); + Assert.assertFalse(userInfoEntityOptional.isPresent()); + } +} diff --git a/src/test/java/org/apache/pulsar/manager/service/GithubLoginServiceImplTest.java b/src/test/java/org/apache/pulsar/manager/service/GithubLoginServiceImplTest.java new file mode 100644 index 0000000..4dadd54 --- /dev/null +++ b/src/test/java/org/apache/pulsar/manager/service/GithubLoginServiceImplTest.java @@ -0,0 +1,129 @@ +/** + * 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.apache.pulsar.manager.service; + +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import org.apache.pulsar.manager.PulsarManagerApplication; +import org.apache.pulsar.manager.entity.UserInfoEntity; +import org.apache.pulsar.manager.profiles.HerdDBTestProfile; +import org.apache.pulsar.manager.utils.HttpUtil; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.UnsupportedEncodingException; +import java.util.Map; + +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(SpringRunner.class) +@PowerMockIgnore( {"javax.*", "sun.*", "com.sun.*", "org.xml.*", "org.w3c.*"}) +@PrepareForTest(HttpUtil.class) +@SpringBootTest( + classes = { + PulsarManagerApplication.class, + HerdDBTestProfile.class + } +) +@ActiveProfiles("test") +public class GithubLoginServiceImplTest { + + @Autowired + private ThirdPartyLoginService thirdPartyLoginService; + + @Value("${github.client.id}") + private String githubClientId; + + @Value("${github.client.secret}") + private String githubClientSecret; + + @Value("${github.oauth.host}") + private String githubAuthHost; + + @Value("${github.user.info}") + private String githubUserInfo; + + @Test + public void getAuthTokenTest() throws UnsupportedEncodingException { + PowerMockito.mockStatic(HttpUtil.class); + Map<String, String> header = Maps.newHashMap(); + header.put("Content-Type", "application/json"); + header.put("Accept", "application/json"); + Map<String, String> parameters = Maps.newHashMap(); + + // Test no code + String noCodeResult = thirdPartyLoginService.getAuthToken(parameters); + Assert.assertNull(noCodeResult); + + // Test with code + parameters.put("code", "test-code"); + Gson gson = new Gson(); + Map<String, String> body = Maps.newHashMap(); + body.put("code", parameters.get("code")); + body.put("client_id", githubClientId); + body.put("client_secret", githubClientSecret); + PowerMockito.when(HttpUtil.doPost(githubAuthHost, header, gson.toJson(body))) + .thenReturn("{" + + "\"access_token\": \"e72e16c7e42f292c6912e7710c838347ae178b4a\"," + + "\"scope\": \"repo,gist\"," + + "\"token_type\": \"bearer\"" + + "}"); + String withCodeResult = thirdPartyLoginService.getAuthToken(parameters); + Assert.assertEquals(withCodeResult, "e72e16c7e42f292c6912e7710c838347ae178b4a"); + } + + @Test + public void getUserInfoTest() { + + Map<String, String> authenticationMap = Maps.newHashMap(); + UserInfoEntity noTokenUserInfoEntity = thirdPartyLoginService.getUserInfo(authenticationMap); + + Assert.assertEquals(noTokenUserInfoEntity, null); + + authenticationMap.put("access_token", "test-user-token"); + PowerMockito.mockStatic(HttpUtil.class); + Map<String, String> header = Maps.newHashMap(); + header.put("Content-Type", "application/json"); + header.put("Authorization", "token test-user-token"); + PowerMockito.when(HttpUtil.doGet(githubUserInfo, header)) + .thenReturn(null); + UserInfoEntity withTokenNullUserInfoEntity = thirdPartyLoginService.getUserInfo(authenticationMap); + Assert.assertNull(withTokenNullUserInfoEntity); + PowerMockito.when(HttpUtil.doGet(githubUserInfo, header)) + .thenReturn("{\n" + + "\t\"login\": \"test1\",\n" + + "\t\"company\": bj,\n" + + "\t\"location\": \"nw\",\n" + + "\t\"email\": \"t...@apache.org\",\n" + + "\t\"bio\": \"this is description\"" + + "}"); + UserInfoEntity withTokenUserInfoEntity = thirdPartyLoginService.getUserInfo(authenticationMap); + Assert.assertEquals(withTokenUserInfoEntity.getEmail(), "t...@apache.org"); + Assert.assertEquals(withTokenUserInfoEntity.getName(), "test1"); + Assert.assertEquals(withTokenUserInfoEntity.getCompany(), "bj"); + Assert.assertEquals(withTokenUserInfoEntity.getDescription(), "this is description"); + Assert.assertEquals(withTokenUserInfoEntity.getLocation(), "nw"); + Assert.assertEquals(withTokenUserInfoEntity.getAccessToken(), "test-user-token"); + } +}