diff options
author | Vitaly Takmazov | 2018-06-21 21:30:57 +0300 |
---|---|---|
committer | Vitaly Takmazov | 2018-06-21 21:30:57 +0300 |
commit | 85ff16fe77a45af9baac7581c2757b1ee59767d6 (patch) | |
tree | 931c38492c5fc516ad389ac6b4f2134a81889770 /juick-www/src/main/java/com/juick/www | |
parent | 248c05840275516cbc5c99c8e5d87d007ca37284 (diff) |
initial Facebook login
Diffstat (limited to 'juick-www/src/main/java/com/juick/www')
7 files changed, 0 insertions, 687 deletions
diff --git a/juick-www/src/main/java/com/juick/www/Utils.java b/juick-www/src/main/java/com/juick/www/Utils.java deleted file mode 100644 index 3b1932d0b..000000000 --- a/juick-www/src/main/java/com/juick/www/Utils.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008-2017, 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 <http://www.gnu.org/licenses/>. - */ -package com.juick.www; - -import javax.servlet.http.HttpServletRequest; -import java.util.Optional; - -/** - * - * @author Ugnich Anton - */ -public class Utils { - - - public static String encodeSphinx(String str) { - return str.replaceAll("@", "\\\\@") - .replaceAll("\\'", "\\\\'"); - } - - /** - * Returns the viewName to return for coming back to the sender url - * - * @param request Instance of {@link HttpServletRequest} or use an injected instance - * @return Optional with the view name. Recomended to use an alternativa url with - * {@link Optional#orElse(java.lang.Object)} - */ - public static Optional<String> getPreviousPageByRequest(HttpServletRequest request) - { - return Optional.ofNullable(request.getHeader("Referer")).map(requestUrl -> "redirect:" + requestUrl); - } -} diff --git a/juick-www/src/main/java/com/juick/www/controllers/SocialLogin.java b/juick-www/src/main/java/com/juick/www/controllers/SocialLogin.java deleted file mode 100644 index 7be837486..000000000 --- a/juick-www/src/main/java/com/juick/www/controllers/SocialLogin.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 2008-2017, 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 <http://www.gnu.org/licenses/>. - */ -package com.juick.www.controllers; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.scribejava.apis.FacebookApi; -import com.github.scribejava.apis.TwitterApi; -import com.github.scribejava.apis.VkontakteApi; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.*; -import com.github.scribejava.core.oauth.OAuth10aService; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.UserUtils; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.www.Utils; -import com.juick.www.facebook.User; -import com.juick.www.vk.UsersResponse; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.codec.digest.HmacAlgorithms; -import org.apache.commons.codec.digest.HmacUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class SocialLogin { - - private static final Logger logger = LoggerFactory.getLogger(SocialLogin.class); - - @Value("${facebook_appid:appid}") - private String FACEBOOK_APPID; - @Value("${facebook_secret:secret}") - private String FACEBOOK_SECRET; - private static final String FACEBOOK_REDIRECT = "https://juick.com/_fblogin"; - 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 facebookBuilder, twitterBuilder, vkBuilder; - - @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; - - @Inject - private CrosspostService crosspostService; - @Inject - private UserService userService; - @Inject - private EmailService emailService; - @Inject - private TelegramService telegramService; - - @PostConstruct - public void init() { - facebookBuilder = new ServiceBuilder(FACEBOOK_APPID); - twitterBuilder = new ServiceBuilder(twitterConsumerKey); - vkBuilder = new ServiceBuilder(VK_APPID); - } - - @GetMapping("/_fblogin") - protected String doFacebookLogin(HttpServletRequest request, - @RequestParam(required = false) String code, - @RequestParam(required = false) String state, - HttpServletResponse response) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - String fbstate = UUID.randomUUID().toString(); - crosspostService.addFacebookState(fbstate); - OAuth20Service facebookAuthService = facebookBuilder - .apiSecret(FACEBOOK_SECRET) - .callback(FACEBOOK_REDIRECT) - .scope("email") - .state(fbstate) - .build(FacebookApi.instance()); - return "redirect:" + facebookAuthService.getAuthorizationUrl(); - } - - if (!crosspostService.verifyFacebookState(state)) { - logger.error("state is missing"); - throw new HttpBadRequestException(); - } - OAuth20Service facebookService = facebookBuilder - .apiKey(FACEBOOK_APPID) - .apiSecret(FACEBOOK_SECRET) - .callback(FACEBOOK_REDIRECT) - .scope("email") - .state(state) - .build(FacebookApi.instance()); - OAuth2AccessToken token = facebookService.getAccessToken(code); - final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v2.10/me?fields=id,name,link,verified,email"); - facebookService.signRequest(token, meRequest); - String graph = facebookService.execute(meRequest).getBody(); - if (StringUtils.isBlank(graph)) { - logger.error("FACEBOOK GRAPH ERROR"); - throw new HttpBadRequestException(); - } - User fb = jsonMapper.readValue(graph, User.class); - long fbID = NumberUtils.toLong(fb.getId(), 0); - if (fbID == 0 || StringUtils.isBlank(fb.getName()) || StringUtils.isBlank(fb.getLink())) { - logger.error("Missing required fields, id: {}, name: {}, link: {}", fbID, fb.getName(), fb.getLink()); - throw new HttpBadRequestException(); - } - - int uid = crosspostService.getUIDbyFBID(fbID); - if (uid > 0) { - if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName(), fb.getLink())) { - logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken()); - throw new HttpBadRequestException(); - } - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:/"; - } else if (fb.getVerified()) { - if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName(), fb.getLink())) { - if (StringUtils.isNotEmpty(fb.getEmail())) { - logger.info("found {} for facebook user {}", fb.getEmail(), fb.getLink()); - Integer userId = crosspostService.getUIDbyFBID(fbID); - if (!emailService.getEmails(userId, false).contains(fb.getEmail())) { - emailService.addEmail(userId, fb.getEmail()); - } - } - logger.info("email not found for facebook user {}", fb.getLink()); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=fb&hash=" + state; - } else { - logger.error("Facebook account is not verified, id: {}", fbID); - throw new HttpBadRequestException(); - } - } - @GetMapping("/_twitter") - protected void doTwitterLogin(HttpServletRequest request, HttpServletResponse response) - throws IOException, ExecutionException, InterruptedException { - String hash = StringUtils.EMPTY, request_token = StringUtils.EMPTY, request_token_secret = StringUtils.EMPTY; - String verifier = request.getParameter("oauth_verifier"); - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("hash")) { - hash = cookie.getValue(); - } - if (cookie.getName().equals("request_token")) { - request_token = cookie.getValue(); - } - if (cookie.getName().equals("request_token_secret")) { - request_token_secret = cookie.getValue(); - } - } - com.juick.User user = UserUtils.getCurrentUser(); - OAuth10aService oAuthService = twitterBuilder - .apiSecret(twitterConsumerSecret) - .callback("http://juick.com/_twitter") - .build(TwitterApi.instance()); - - if (request_token.isEmpty() && request_token_secret.isEmpty() - && (verifier == null || verifier.isEmpty())) { - OAuth1RequestToken requestToken = oAuthService.getRequestToken(); - String authUrl = oAuthService.getAuthorizationUrl(requestToken); - response.addCookie(new Cookie("request_token", requestToken.getToken())); - response.addCookie(new Cookie("request_token_secret", requestToken.getTokenSecret())); - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", authUrl); - } else { - if (verifier != null && verifier.length() > 0) { - OAuth1RequestToken requestToken = new OAuth1RequestToken(request_token, request_token_secret); - OAuth1AccessToken accessToken = oAuthService.getAccessToken(requestToken, verifier); - OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, TWITTER_VERIFY_URL); - oAuthService.signRequest(accessToken, oAuthRequest); - com.juick.www.twitter.User twitterUser = jsonMapper.readValue(oAuthService.execute(oAuthRequest).getBody(), - com.juick.www.twitter.User.class); - if (userService.linkTwitterAccount(user, accessToken.getToken(), accessToken.getTokenSecret(), - twitterUser.getScreenName())) { - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", "http://juick.com/settings"); - } else { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - } - } - @GetMapping("/_vklogin") - protected String doVKLogin(HttpServletRequest request, - @RequestParam(required = false) String code, - @RequestParam(required = false) String state, - @CookieValue(required = false) String vkstate, - HttpServletResponse response) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - vkstate = UUID.randomUUID().toString(); - Cookie c = new Cookie("vkstate", vkstate); - response.addCookie(c); - OAuth20Service vkAuthService = vkBuilder - .apiSecret(VK_SECRET) - .scope("friends,wall,offline") - .state(vkstate) - .callback(VK_REDIRECT) - .build(VkontakteApi.instance()); - return "redirect:" + vkAuthService.getAuthorizationUrl(); - } - - if (StringUtils.isBlank(vkstate) || !vkstate.equals(state)) { - throw new HttpBadRequestException(); - } else { - Cookie c = new Cookie("vkstate", "-"); - c.setMaxAge(0); - response.addCookie(c); - } - - OAuth20Service vkService = vkBuilder - .apiKey(VK_APPID) - .apiSecret(VK_SECRET) - .build(VkontakteApi.instance()); - OAuth2AccessToken token = vkService.getAccessToken(code); - - OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.73"); - vkService.signRequest(token, meRequest); - String graph = vkService.execute(meRequest).getBody(); - - com.juick.www.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).getUsers().get(0); - String vkName = jsonUser.getFirstName() + " " + jsonUser.getLastName(); - String vkLink = jsonUser.getScreenName(); - - if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) { - logger.error("vk user error"); - throw new HttpBadRequestException(); - } - - Long vkID = NumberUtils.toLong(jsonUser.getId(), 0); - int uid = crosspostService.getUIDbyVKID(vkID); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return Utils.getPreviousPageByRequest(request).orElse("redirect:/"); - } else { - String loginhash = UUID.randomUUID().toString(); - if (!crosspostService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) { - logger.error("create vk user error"); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=vk&hash=" + loginhash; - } - } - - @GetMapping("/_tglogin") - public String doDurovLogin(HttpServletRequest request, - @RequestParam Map<String, String> params, - HttpServletResponse response) { - String dataCheckString = params.entrySet().stream() - .filter(p -> !p.getKey().equals("hash")) - .sorted(Map.Entry.comparingByKey()) - .map(p -> p.getKey() + "=" + p.getValue()) - .collect(Collectors.joining("\n")); - String hash = params.get("hash"); - byte[] secretKey = DigestUtils.sha256(telegramToken); - String resultString = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secretKey).hmacHex(dataCheckString); - if (hash.equals(resultString)) { - Long tgUser = Long.valueOf(params.get("id")); - int uid = telegramService.getUser(tgUser); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return Utils.getPreviousPageByRequest(request).orElse("redirect:/"); - } else { - String username = StringUtils.defaultString(params.get("username"), params.get("first_name")); - telegramService.createTelegramUser(tgUser, username); - return "redirect:/signup?type=durov&hash=" + userService.getSignUpHashByTelegramID(tgUser, username); - } - } else { - logger.warn("invalid tg hash {} for {}", resultString, hash); - } - throw new HttpBadRequestException(); - } -} diff --git a/juick-www/src/main/java/com/juick/www/facebook/User.java b/juick-www/src/main/java/com/juick/www/facebook/User.java deleted file mode 100644 index b56c4ee31..000000000 --- a/juick-www/src/main/java/com/juick/www/facebook/User.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2008-2017, 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.www.facebook; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Created by vitalyster on 28.11.2016. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class User { - private String id; - private String name; - private String link; - private boolean verified; - private String firstName; - private String lastName; - private String gender; - private String locale; - private String timezone; - private String updatedTime; - private String email; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getLink() { - return link; - } - - public void setLink(String link) { - this.link = link; - } - - public boolean getVerified() { - return verified; - } - - public void setVerified(boolean verified) { - this.verified = verified; - } - - @JsonProperty("first_name") - public String getFirstName() { - return firstName; - } - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getGender() { - return gender; - } - - public void setGender(String gender) { - this.gender = gender; - } - - @JsonProperty("last_name") - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getLocale() { - return locale; - } - - public void setLocale(String locale) { - this.locale = locale; - } - - public String getTimezone() { - return timezone; - } - - public void setTimezone(String timezone) { - this.timezone = timezone; - } - - @JsonProperty("updated_time") - public String getUpdatedTime() { - return updatedTime; - } - - public void setUpdatedTime(String updatedTime) { - this.updatedTime = updatedTime; - } - - public String getEmail() { - return email; - } -} diff --git a/juick-www/src/main/java/com/juick/www/twitter/User.java b/juick-www/src/main/java/com/juick/www/twitter/User.java deleted file mode 100644 index a92daa92e..000000000 --- a/juick-www/src/main/java/com/juick/www/twitter/User.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2008-2017, 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.www.twitter; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Created by vitalyster on 28.11.2016. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class User { - private String screenName; - - @JsonProperty("screen_name") - public String getScreenName() { - return screenName; - } - - public void setScreenName(String screenName) { - this.screenName = screenName; - } -} diff --git a/juick-www/src/main/java/com/juick/www/vk/Token.java b/juick-www/src/main/java/com/juick/www/vk/Token.java deleted file mode 100644 index bcc366390..000000000 --- a/juick-www/src/main/java/com/juick/www/vk/Token.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2008-2017, 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.www.vk; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Created by vitalyster on 28.11.2016. - */ -public class Token { - private Long userId; - private String accessToken; - private String expiresIn; - - @JsonProperty("user_id") - public Long getUserId() { - return userId; - } - - public void setUserId(Long userId) { - this.userId = userId; - } - - @JsonProperty("access_token") - public String getAccessToken() { - return accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - @JsonProperty("expires_in") - public String getExpiresIn() { - return expiresIn; - } - - public void setExpiresIn(String expiresIn) { - this.expiresIn = expiresIn; - } -} diff --git a/juick-www/src/main/java/com/juick/www/vk/User.java b/juick-www/src/main/java/com/juick/www/vk/User.java deleted file mode 100644 index 749631bb6..000000000 --- a/juick-www/src/main/java/com/juick/www/vk/User.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2008-2017, 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.www.vk; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Created by vitalyster on 28.11.2016. - */ -public class User { - private String id; - private String firstName; - private String lastName; - private String screenName; - - @JsonProperty("first_name") - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - @JsonProperty("last_name") - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - @JsonProperty("screen_name") - public String getScreenName() { - return screenName; - } - - public void setScreenName(String screenName) { - this.screenName = screenName; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } -} diff --git a/juick-www/src/main/java/com/juick/www/vk/UsersResponse.java b/juick-www/src/main/java/com/juick/www/vk/UsersResponse.java deleted file mode 100644 index 6f645451e..000000000 --- a/juick-www/src/main/java/com/juick/www/vk/UsersResponse.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2008-2017, 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 <http://www.gnu.org/licenses/>. - */ - -package com.juick.www.vk; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -/** - * Created by vitalyster on 28.11.2016. - */ -public class UsersResponse { - private List<User> users; - - @JsonProperty("response") - public List<User> getUsers() { - return users; - } - - public void setUsers(List<User> users) { - this.users = users; - } -} |