Skip to content

Commit a6c4c74

Browse files
committed
Added new version Signup service
1 parent 70d5bb7 commit a6c4c74

1 file changed

Lines changed: 359 additions & 0 deletions

File tree

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
package it.eng.spagobi.commons.services;
2+
3+
import java.awt.image.BufferedImage;
4+
import java.io.ByteArrayOutputStream;
5+
import java.net.URL;
6+
import java.nio.charset.StandardCharsets;
7+
import java.util.Base64;
8+
import java.util.HashMap;
9+
import java.util.HashSet;
10+
import java.util.Map;
11+
import java.util.Set;
12+
13+
import javax.imageio.ImageIO;
14+
import javax.servlet.http.HttpServletRequest;
15+
import javax.ws.rs.GET;
16+
import javax.ws.rs.POST;
17+
import javax.ws.rs.Path;
18+
import javax.ws.rs.Produces;
19+
import javax.ws.rs.core.Context;
20+
import javax.ws.rs.core.MediaType;
21+
import javax.ws.rs.core.Response;
22+
23+
import org.apache.commons.lang3.StringUtils;
24+
import org.apache.log4j.Logger;
25+
26+
import com.auth0.jwt.exceptions.TokenExpiredException;
27+
import com.google.common.io.Resources;
28+
29+
import it.eng.knowage.mailsender.IMailSender;
30+
import it.eng.knowage.mailsender.dto.MessageMailDto;
31+
import it.eng.knowage.mailsender.dto.ProfileNameMailEnum;
32+
import it.eng.knowage.mailsender.dto.TypeMailEnum;
33+
import it.eng.knowage.mailsender.factory.FactoryMailSender;
34+
import it.eng.spago.error.EMFUserError;
35+
import it.eng.spagobi.commons.SingletonConfig;
36+
import it.eng.spagobi.commons.bo.Role;
37+
import it.eng.spagobi.commons.dao.DAOFactory;
38+
import it.eng.spagobi.commons.dao.IRoleDAO;
39+
import it.eng.spagobi.commons.metadata.SbiCommonInfo;
40+
import it.eng.spagobi.commons.metadata.SbiExtRoles;
41+
import it.eng.spagobi.commons.validation.PasswordChecker;
42+
import it.eng.spagobi.profiling.bean.SbiUser;
43+
import it.eng.spagobi.profiling.bean.SbiUserAttributes;
44+
import it.eng.spagobi.profiling.bean.SbiUserAttributesId;
45+
import it.eng.spagobi.profiling.dao.ISbiAttributeDAO;
46+
import it.eng.spagobi.profiling.dao.ISbiUserDAO;
47+
import it.eng.spagobi.security.Password;
48+
import it.eng.spagobi.services.rest.annotations.PublicService;
49+
import it.eng.spagobi.signup.validation.SignupJWTTokenManager;
50+
import net.logicsquad.nanocaptcha.image.ImageCaptcha;
51+
import net.logicsquad.nanocaptcha.image.filter.FishEyeImageFilter;
52+
53+
@Path("2.0/signup")
54+
public class SignupResource {
55+
56+
private static final Logger LOGGER = Logger.getLogger(SignupResource.class);
57+
58+
@GET
59+
@Path("/captcha")
60+
@PublicService
61+
@Produces(MediaType.APPLICATION_JSON)
62+
public Response getCaptcha() {
63+
try {
64+
int width = 200;
65+
int height = 75;
66+
67+
ImageCaptcha imageCaptcha = new ImageCaptcha.Builder(width, height).addContent().addBackground().addFilter(new FishEyeImageFilter()).build();
68+
69+
BufferedImage image = imageCaptcha.getImage();
70+
String content = imageCaptcha.getContent();
71+
72+
String base64Image;
73+
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
74+
ImageIO.write(image, "png", baos);
75+
base64Image = Base64.getEncoder().encodeToString(baos.toByteArray());
76+
}
77+
78+
Map<String, String> response = new HashMap<>();
79+
response.put("image", base64Image);
80+
response.put("content", Password.hashPassword(content));
81+
82+
return Response.ok(response).build();
83+
84+
} catch (Exception e) {
85+
LOGGER.error("Error while generating captcha", e);
86+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", "Error while generating captcha")).build();
87+
}
88+
}
89+
90+
@POST
91+
@Path("/create")
92+
@PublicService
93+
@Produces(MediaType.APPLICATION_JSON)
94+
public Response create(@Context HttpServletRequest req, Map<String, String> payload) {
95+
LOGGER.debug("IN");
96+
97+
try {
98+
// Check if signup is enabled
99+
String strActiveSignup = SingletonConfig.getInstance().getConfigValue("SPAGOBI.SECURITY.ACTIVE_SIGNUP_FUNCTIONALITY");
100+
boolean activeSignup = "true".equalsIgnoreCase(strActiveSignup);
101+
if (!activeSignup) {
102+
LOGGER.error("Attempt to register with signup not active");
103+
return Response.status(Response.Status.FORBIDDEN).entity(Map.of("error", "Signup is not enabled")).build();
104+
}
105+
106+
// Extract parameters
107+
String username = payload.get("username");
108+
String password = payload.get("password");
109+
String confirmPassword = payload.get("confirmPassword");
110+
String name = payload.get("name");
111+
String surname = payload.get("surname");
112+
String email = payload.get("email");
113+
String captcha = payload.get("captcha");
114+
String content = payload.get("content");
115+
116+
// Validate inputs
117+
Response validationError = validateSignupInput(username, password, confirmPassword, email, captcha, content);
118+
if (validationError != null) {
119+
return validationError;
120+
}
121+
122+
// Get default tenant
123+
String defaultTenant = getDefaultTenant();
124+
125+
ISbiUserDAO userDao = DAOFactory.getSbiUserDAO();
126+
Integer existingUserId = userDao.isUserIdAlreadyInUse(username);
127+
boolean userRegistrationFromExpiredToken = false;
128+
129+
// Check if user already exists
130+
if (existingUserId != null) {
131+
SbiUser sbiUser = userDao.loadSbiUserById(existingUserId);
132+
boolean matchingEmailAddress = false;
133+
134+
if (sbiUser != null) {
135+
Set<SbiUserAttributes> userAttributes = sbiUser.getSbiUserAttributeses();
136+
for (SbiUserAttributes sbiUserAttributes : userAttributes) {
137+
if (sbiUserAttributes.getSbiAttribute().getAttributeName().equals("email") && sbiUserAttributes.getAttributeValue().equals(email)) {
138+
matchingEmailAddress = true;
139+
break;
140+
}
141+
}
142+
}
143+
144+
userRegistrationFromExpiredToken = matchingEmailAddress && sbiUser.getDtLastAccess() == null;
145+
146+
if (!userRegistrationFromExpiredToken) {
147+
LOGGER.error("Username already in use");
148+
return Response.status(Response.Status.CONFLICT).entity(Map.of("error", "Username already in use")).build();
149+
}
150+
}
151+
152+
// Create user
153+
SbiUser user = new SbiUser();
154+
user.setUserId(username);
155+
user.setPassword(Password.hashPassword(password));
156+
user.setFullName(name + " " + surname);
157+
user.getCommonInfo().setOrganization(defaultTenant);
158+
user.getCommonInfo().setUserIn(username);
159+
user.setFlgPwdBlocked(true);
160+
161+
// Set tenant and role
162+
Set<SbiExtRoles> roles = new HashSet<>();
163+
SbiExtRoles r = new SbiExtRoles();
164+
String defaultRole = SingletonConfig.getInstance().getConfigValue("SPAGOBI.SECURITY.DEFAULT_ROLE_ON_SIGNUP");
165+
166+
IRoleDAO roleDAO = DAOFactory.getRoleDAO();
167+
roleDAO.setTenant(defaultTenant);
168+
Role signupRole = roleDAO.loadByName(defaultRole);
169+
170+
if (signupRole == null) {
171+
LOGGER.error("Invalid role " + defaultRole + " for the new user");
172+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", "Invalid default role configuration")).build();
173+
}
174+
175+
r.changeExtRoleId(signupRole.getId());
176+
r.getCommonInfo().setOrganization(defaultTenant);
177+
roles.add(r);
178+
user.setSbiExtUserRoleses(roles);
179+
180+
// Set user attributes
181+
Set<SbiUserAttributes> attributes = new HashSet<>();
182+
ISbiAttributeDAO attrDao = DAOFactory.getSbiAttributeDAO();
183+
attrDao.setTenant(defaultTenant);
184+
185+
addAttribute(attributes, attrDao.loadSbiAttributeByName("email").getAttributeId(), email, defaultTenant);
186+
187+
user.setSbiUserAttributeses(attributes);
188+
if (userRegistrationFromExpiredToken) {
189+
user.changeId(existingUserId);
190+
}
191+
192+
int id = userDao.fullSaveOrUpdateSbiUser(user);
193+
LOGGER.debug("User [" + username + "] successfully created with id [" + id + "]");
194+
195+
// Send activation email
196+
try {
197+
sendActivationEmail(req, user, username);
198+
} catch (Exception e) {
199+
LOGGER.error("Cannot send activation email to [" + email + "]", e);
200+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", "Error while sending activation email")).build();
201+
}
202+
203+
LOGGER.debug("OUT");
204+
return Response.ok(Map.of("message", "User registered successfully. Please check your email for activation")).build();
205+
206+
} catch (Exception e) {
207+
LOGGER.error("Error during user creation", e);
208+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", "An error occurred during registration")).build();
209+
}
210+
}
211+
212+
@POST
213+
@Path("/active")
214+
@PublicService
215+
@Produces(MediaType.APPLICATION_JSON)
216+
public Response active(@Context HttpServletRequest req, Map<String, String> payload) {
217+
LOGGER.debug("IN");
218+
String token = payload.get("token");
219+
220+
if (StringUtils.isBlank(token)) {
221+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Token is mandatory")).build();
222+
}
223+
224+
try {
225+
String userId = SignupJWTTokenManager.verifyJWTToken(token);
226+
227+
ISbiUserDAO userDao = DAOFactory.getSbiUserDAO();
228+
SbiUser user = userDao.loadSbiUserByUserId(userId);
229+
230+
if (user == null) {
231+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "User not found")).build();
232+
}
233+
234+
if (!user.getFlgPwdBlocked()) {
235+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "User already active")).build();
236+
}
237+
238+
user.setFlgPwdBlocked(false);
239+
userDao.updateSbiUser(user, null);
240+
241+
LOGGER.debug("OUT");
242+
return Response.ok(Map.of("message", "User activated successfully")).build();
243+
244+
} catch (TokenExpiredException te) {
245+
LOGGER.error("Expired Token [" + token + "]", te);
246+
return Response.status(Response.Status.UNAUTHORIZED).entity(Map.of("error", "Token expired", "expired", true)).build();
247+
} catch (Exception e) {
248+
LOGGER.error("Generic token validation error [" + token + "]", e);
249+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Invalid token")).build();
250+
}
251+
}
252+
253+
private String getDefaultTenant() {
254+
String defaultTenant = SingletonConfig.getInstance().getConfigValue("SPAGOBI.SECURITY.DEFAULT_TENANT_ON_SIGNUP");
255+
if (defaultTenant == null) {
256+
LOGGER.debug("DEFAULT_TENANT_ON_SIGNUP not configured, using default value");
257+
defaultTenant = "DEFAULT_TENANT";
258+
}
259+
LOGGER.debug("Default tenant set to: " + defaultTenant);
260+
return defaultTenant;
261+
}
262+
263+
private Response validateSignupInput(String username, String password, String confirmPassword, String email, String captcha, String content) {
264+
if (StringUtils.isBlank(username)) {
265+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Username is mandatory")).build();
266+
}
267+
268+
if (StringUtils.isBlank(password) || StringUtils.isBlank(confirmPassword)) {
269+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Password and confirm password are mandatory")).build();
270+
}
271+
272+
if (!password.equals(confirmPassword)) {
273+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Passwords do not match")).build();
274+
}
275+
276+
try {
277+
PasswordChecker.getInstance().isValid(password, password);
278+
} catch (EMFUserError e) {
279+
LOGGER.error("Password is not valid", e);
280+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Password is not valid")).build();
281+
} catch (Exception e) {
282+
LOGGER.error("Password is not valid", e);
283+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Password is not valid")).build();
284+
}
285+
286+
if (StringUtils.isBlank(email)) {
287+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Email is mandatory")).build();
288+
}
289+
290+
if (StringUtils.isBlank(captcha)) {
291+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Captcha is required")).build();
292+
}
293+
294+
if (StringUtils.isBlank(content)) {
295+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Captcha content is missing")).build();
296+
}
297+
298+
if (!content.equals(Password.hashPassword(captcha))) {
299+
return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", "Invalid captcha")).build();
300+
}
301+
302+
return null;
303+
}
304+
305+
private void sendActivationEmail(HttpServletRequest req, SbiUser user, String username) throws Exception {
306+
String host = req.getServerName();
307+
int port = req.getServerPort();
308+
309+
String mailText = Resources.toString(getClass().getResource("/templates/confirmationMailTemplate.html"), StandardCharsets.UTF_8);
310+
String subject = "Activation Request";
311+
312+
String token = SignupJWTTokenManager.createJWTToken(user.getUserId());
313+
String version = SbiCommonInfo.getVersion().substring(0, SbiCommonInfo.getVersion().lastIndexOf("."));
314+
315+
String urlString = req.getContextPath() + "/restful-services/signup/prepareActive?token=" + token + "&version=" + version;
316+
URL url = new URL(req.getScheme(), host, port, urlString);
317+
318+
// Replace placeholders in template
319+
mailText = mailText.replace("%%WELCOME%%", "Welcome to our platform");
320+
mailText = mailText.replace("%%USERNAME%%", username);
321+
mailText = mailText.replace("%%THANKS_MESSAGE%%", "Thank you for registering");
322+
mailText = mailText.replace("%%WELCOME_MESSAGE%%", "Please activate your account by clicking the link below");
323+
mailText = mailText.replace("%%URL%%", url.toString());
324+
mailText = mailText.replace("%%URL_LABEL%%", "Activate Account");
325+
mailText = mailText.replace("%%BOOKMARK%%", "Bookmark our website");
326+
mailText = mailText.replace("%%QA%%", "Visit our Q&A section");
327+
mailText = mailText.replace("%%GITHUB%%", "Visit our GitHub");
328+
mailText = mailText.replace("%%DOCUMENTATION%%", "Read the documentation");
329+
330+
LOGGER.debug("Activation url is equal to [" + url.toExternalForm() + "]");
331+
LOGGER.debug("Activation mail for user [" + username + "] successfully prepared");
332+
333+
sendMail(user.getSbiUserAttributeses().iterator().next().getAttributeValue(), subject, mailText);
334+
}
335+
336+
private void addAttribute(Set<SbiUserAttributes> attributes, int attrId, String attrValue, String defaultTenant) {
337+
if (attrValue != null && !attrValue.isEmpty()) {
338+
SbiUserAttributes a = new SbiUserAttributes();
339+
a.getCommonInfo().setOrganization(defaultTenant);
340+
SbiUserAttributesId id = new SbiUserAttributesId(attrId);
341+
a.setId(id);
342+
a.setAttributeValue(attrValue);
343+
attributes.add(a);
344+
}
345+
}
346+
347+
private void sendMail(String emailAddress, String subject, String emailContent) throws Exception {
348+
MessageMailDto messageMailDto = new MessageMailDto();
349+
messageMailDto.setProfileName(ProfileNameMailEnum.USER);
350+
messageMailDto.setTypeMailEnum(TypeMailEnum.CONTENT);
351+
messageMailDto.setSubject(subject);
352+
messageMailDto.setText(emailContent);
353+
messageMailDto.setContentType("text/html");
354+
messageMailDto.setRecipients(new String[] { emailAddress });
355+
356+
FactoryMailSender.getMailSender(SingletonConfig.getInstance().getConfigValue(IMailSender.MAIL_SENDER)).sendMail(messageMailDto);
357+
}
358+
359+
}

0 commit comments

Comments
 (0)