From 10dcb7324fac83c0190ff4a842360a035449f278 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 20 Jun 2023 07:10:16 +0300 Subject: VK: read premium status using Callback API --- .../java/com/juick/www/api/webhooks/VkWebhook.java | 76 ++++++++++++++ .../com/juick/www/controllers/SocialLogin.java | 109 +++++++-------------- 2 files changed, 113 insertions(+), 72 deletions(-) create mode 100644 src/main/java/com/juick/www/api/webhooks/VkWebhook.java (limited to 'src/main/java/com/juick/www') diff --git a/src/main/java/com/juick/www/api/webhooks/VkWebhook.java b/src/main/java/com/juick/www/api/webhooks/VkWebhook.java new file mode 100644 index 00000000..9e4477b1 --- /dev/null +++ b/src/main/java/com/juick/www/api/webhooks/VkWebhook.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008-2023, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.www.api.webhooks; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.juick.service.UserService; +import com.juick.service.VKService; +import com.juick.util.HttpBadRequestException; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.inject.Inject; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +@RestController +public class VkWebhook { + private static final Logger logger = LoggerFactory.getLogger("VK"); + @Value("${vk_webhook_secret:}") + private String secretKey; + @Value("${vk_webhook_confirmation:}") + private String confirmationCode; + @Inject + private ObjectMapper jsonMapper; + @Inject + private UserService userService; + @Inject + private VKService vkService; + + @PostMapping(value = "/api/_vk") + public String processUpdate(InputStream body) throws IOException { + String data = IOUtils.toString(body, StandardCharsets.UTF_8); + logger.debug("Data: {}", data); + JsonNode json = jsonMapper.readTree(data); + var type = json.get("type").asText(); + var secret = json.get("secret").asText(""); + logger.info("Event received: {}, secret: {}", type, secret); + if (type.equals("confirmation")) { + return confirmationCode; + } else { + if (secretKey.equals(secret)) { + if (type.startsWith("donut_")) { + var vkId = json.get("object").get("user_id").asLong(0); + var userId = userService.getUIDbyVKID(vkId); + if (userId > 0) { + vkService.updatePremiumStatus(userId); + } + } + return "ok"; + } else { + throw new HttpBadRequestException(); + } + } + } +} diff --git a/src/main/java/com/juick/www/controllers/SocialLogin.java b/src/main/java/com/juick/www/controllers/SocialLogin.java index b0738fea..0cac26c5 100644 --- a/src/main/java/com/juick/www/controllers/SocialLogin.java +++ b/src/main/java/com/juick/www/controllers/SocialLogin.java @@ -27,6 +27,7 @@ import com.juick.model.ext.vk.UsersResponse; import com.juick.service.EmailService; import com.juick.service.TelegramService; import com.juick.service.UserService; +import com.juick.service.VKService; import com.juick.service.security.entities.JuickUser; import com.juick.util.HttpBadRequestException; @@ -81,21 +82,17 @@ public class SocialLogin { private String FACEBOOK_SECRET; @Value("${ap_base_uri:http://localhost:8080/}") private String baseUri; - private static final String VK_REDIRECT = "http://juick.com/_vklogin"; private static final String TWITTER_VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; @Inject private ObjectMapper jsonMapper; private ServiceBuilder twitterBuilder; - private OAuth20Service facebookAuthService, vkAuthService, appleSignInService; - + private OAuth20Service facebookAuthService, appleSignInService; + @Inject + private VKService vkService; @Value("${twitter_consumer_key:appid}") private String twitterConsumerKey; @Value("${twitter_consumer_secret:secret}") private String twitterConsumerSecret; - @Value("${vk_appid:appid}") - private String VK_APPID; - @Value("${vk_secret:secret}") - private String VK_SECRET; @Value("${telegram_token:secret}") private String telegramToken; @Value("${apple_app_id:appid}") @@ -115,13 +112,11 @@ public class SocialLogin { public void init() { ServiceBuilder facebookBuilder = new ServiceBuilder(FACEBOOK_APPID); twitterBuilder = new ServiceBuilder(twitterConsumerKey); - ServiceBuilder vkBuilder = new ServiceBuilder(VK_APPID); + UriComponentsBuilder redirectBuilder = UriComponentsBuilder.fromUriString(baseUri); String facebookRedirectUri = redirectBuilder.replacePath("/_fblogin").build().toUriString(); facebookAuthService = facebookBuilder.apiSecret(FACEBOOK_SECRET).callback(facebookRedirectUri) .defaultScope("email").build(FacebookApi.instance()); - vkAuthService = vkBuilder.apiSecret(VK_SECRET).defaultScope("friends,wall,offline,groups").callback(VK_REDIRECT) - .build(VkontakteApi.instance()); ServiceBuilder appleSignInBuilder = new ServiceBuilder(appleApplicationId); String appleSignInRedirectUri = redirectBuilder.replacePath("/_apple").build().toUriString(); appleSignInService = appleSignInBuilder.callback(appleSignInRedirectUri).defaultScope("email") @@ -249,7 +244,7 @@ public class SocialLogin { vkstate = UUID.randomUUID().toString(); Cookie c = new Cookie("vkstate", vkstate); response.addCookie(c); - return "redirect:" + vkAuthService.getAuthorizationUrl(vkstate); + return "redirect:" + vkService.getVkAuthService().getAuthorizationUrl(vkstate); } if (StringUtils.isBlank(vkstate) || !vkstate.equals(state)) { @@ -259,43 +254,44 @@ public class SocialLogin { c.setMaxAge(0); response.addCookie(c); } - OAuth2AccessToken token = vkAuthService.getAccessToken(code); + OAuth2AccessToken token = vkService.getVkAuthService().getAccessToken(code); OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.131"); - vkAuthService.signRequest(token, meRequest); - Response vkResponse = vkAuthService.execute(meRequest); - if (vkResponse.isSuccessful()) { - String graph = vkResponse.getBody(); - com.juick.model.ext.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).users().stream() - .findFirst().orElseThrow(HttpBadRequestException::new); - String vkName = jsonUser.firstName() + " " + jsonUser.lastName(); - String vkLink = jsonUser.screenName(); - - if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) { - logger.error("vk user error"); - throw new HttpBadRequestException(); - } + vkService.getVkAuthService().signRequest(token, meRequest); + try (Response vkResponse = vkService.getVkAuthService().execute(meRequest)) { + if (vkResponse.isSuccessful()) { + String graph = vkResponse.getBody(); + com.juick.model.ext.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).users().stream() + .findFirst().orElseThrow(HttpBadRequestException::new); + String vkName = jsonUser.firstName() + " " + jsonUser.lastName(); + String vkLink = jsonUser.screenName(); - long vkID = NumberUtils.toLong(jsonUser.id(), 0); - int uid = userService.getUIDbyVKID(vkID); - if (uid > 0) { - userService.updateVkUser(vkID, token.getAccessToken(), vkName, vkLink); - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:/" + Optional.ofNullable(referer).orElse(StringUtils.EMPTY); - } else { - String loginhash = UUID.randomUUID().toString(); - if (!userService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) { - logger.error("create vk user error"); + if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) { + logger.error("vk user error"); throw new HttpBadRequestException(); } - return "redirect:/signup?type=vk&hash=" + loginhash; + + long vkID = NumberUtils.toLong(jsonUser.id(), 0); + int uid = userService.getUIDbyVKID(vkID); + if (uid > 0) { + userService.updateVkUser(vkID, token.getAccessToken(), vkName, vkLink); + Cookie c = new Cookie("hash", userService.getHashByUID(uid)); + c.setMaxAge(50 * 24 * 60 * 60); + response.addCookie(c); + return "redirect:/" + Optional.ofNullable(referer).orElse(StringUtils.EMPTY); + } else { + String loginhash = UUID.randomUUID().toString(); + if (!userService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) { + logger.error("create vk user error"); + throw new HttpBadRequestException(); + } + return "redirect:/signup?type=vk&hash=" + loginhash; + } + } else { + logger.error("vk error {}: {}", vkResponse.getCode(), vkResponse.getBody()); + throw new HttpBadRequestException(); } - } else { - logger.error("vk error {}: {}", vkResponse.getCode(), vkResponse.getBody()); - throw new HttpBadRequestException(); } } @@ -375,35 +371,4 @@ public class SocialLogin { } throw new HttpBadRequestException(); } - - @Scheduled(fixedRate = 3600000) - public void updatePremium() { - userService.getVkTokens(List.of()) - .forEach(vkUser -> { - var userId = userService.getUIDbyVKID(Long.parseLong(vkUser.getLeft())); - if (userId > 0) { - OAuth2AccessToken token = new OAuth2AccessToken(vkUser.getRight()); - OAuthRequest donRequest = new OAuthRequest(Verb.GET, - "https://api.vk.com/method/donut.isDon?owner_id=-67669480&v=5.131"); - vkAuthService.signRequest(token, donRequest); - try { - Response vkResponse = vkAuthService.execute(donRequest); - if (vkResponse.isSuccessful()) { - logger.info(vkResponse.getBody()); - var response = jsonMapper.readTree(vkResponse.getBody()); - if (response.has("response")) { - var isDon = response.get("response").intValue() > 0; - logger.info("{} is Don: {}", vkUser.getLeft(), isDon); - userService.setPremium(userId, isDon); - } else { - // token is expired or does not have "groups" permissions - userService.updateVkToken(userId, ""); - } - } - } catch (Exception e) { - logger.error("Don request error", e); - } - } - }); - } } -- cgit v1.2.3