|
22 | 22 | import org.texttechnologylab.duui.api.controllers.pipelines.DUUIPipelineController; |
23 | 23 | import org.texttechnologylab.duui.api.routes.DUUIRequestHelper; |
24 | 24 |
|
25 | | -import static com.amazonaws.regions.ServiceAbbreviations.Config; |
26 | 25 | import static org.texttechnologylab.duui.api.routes.DUUIRequestHelper.*; |
27 | 26 |
|
28 | 27 | import org.texttechnologylab.duui.api.storage.DUUIMongoDBStorage; |
|
45 | 44 | import spark.Request; |
46 | 45 | import spark.Response; |
47 | 46 |
|
48 | | -import javax.mail.*; |
49 | | -import javax.mail.internet.InternetAddress; |
50 | | -import javax.mail.internet.MimeMessage; |
51 | | - |
52 | 47 |
|
53 | 48 | /** |
54 | 49 | * A Controller for database operations related to the users collection. |
@@ -1420,42 +1415,122 @@ public static boolean verifyActivationCode(String userId, String code) throws Ex |
1420 | 1415 | Filters.gte("expiresAt", now) |
1421 | 1416 | ); |
1422 | 1417 |
|
| 1418 | + log.info("verifyActivationCode: start | userId={} now={}", userId, now); |
| 1419 | + log.debug("verifyActivationCode: filter={}", filter.toString()); |
| 1420 | + |
1423 | 1421 | var update = Updates.combine( |
1424 | 1422 | Updates.set("used", true) |
1425 | 1423 | ); |
1426 | 1424 |
|
1427 | | - var res = col.findOneAndUpdate(filter, update, new FindOneAndUpdateOptions() |
1428 | | - .returnDocument(ReturnDocument.AFTER)); |
| 1425 | + var options = new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER); |
| 1426 | + |
| 1427 | + var res = col.findOneAndUpdate(filter, update, options); |
| 1428 | + |
| 1429 | + if (res != null) { |
| 1430 | + log.info("verifyActivationCode: activation record matched and marked used for userId={}", userId); |
| 1431 | + log.debug("verifyActivationCode: activation record after update={}", res.toString()); |
| 1432 | + |
| 1433 | + try { |
| 1434 | + // Korrigierter Filter: match by _id as ObjectId |
| 1435 | + Bson userFilter = Filters.eq("_id", new ObjectId(userId)); |
| 1436 | + var userDoc = DUUIMongoDBStorage |
| 1437 | + .Users() |
| 1438 | + .findOneAndUpdate(userFilter, Updates.set("activated", true), new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)); |
| 1439 | + |
| 1440 | + if (userDoc != null) { |
| 1441 | + log.info("verifyActivationCode: user document updated, activated=true for userId={}", userId); |
| 1442 | + log.info("verifyActivationCode: updated user document={}", userDoc.toJson()); |
| 1443 | + } else { |
| 1444 | + // Fallback: inspect UpdateResult to get matched/modified counts |
| 1445 | + UpdateResult ur = DUUIMongoDBStorage.Users().updateOne(userFilter, Updates.set("activated", true)); |
| 1446 | + log.warn("verifyActivationCode: findOneAndUpdate returned null for userId={}, fallback updateOne result: matched={}, modified={}, acknowledged={}", |
| 1447 | + userId, ur.getMatchedCount(), ur.getModifiedCount(), ur.wasAcknowledged()); |
| 1448 | + } |
| 1449 | + } catch (IllegalArgumentException iae) { |
| 1450 | + log.error("verifyActivationCode: invalid userId format: {}", userId, iae); |
| 1451 | + } catch (Exception e) { |
| 1452 | + log.error("verifyActivationCode: failed to set activated=true for userId={}", userId, e); |
| 1453 | + } |
| 1454 | + } else { |
| 1455 | + log.info("verifyActivationCode: no activation record found (invalid/expired/already used) for userId={}", userId); |
| 1456 | + } |
1429 | 1457 |
|
1430 | 1458 | return res != null; // true => success; false => invalid/expired/already used |
1431 | 1459 | } |
1432 | 1460 |
|
1433 | | - public static void sendMail(String to, String subject, String body) throws MessagingException { |
1434 | | - String host = Main.config.getSmtpHost(); |
1435 | | - String port = Main.config.getSmtpPort(); |
1436 | | - String user = Main.config.getSmtpUser(); |
1437 | | - String pass = Main.config.getSmtpPassword(); |
1438 | | - String from = Main.config.getSmtpFromEmail(); |
1439 | | - |
1440 | | - Properties props = new Properties(); |
1441 | | - props.put("mail.smtp.auth", "true"); |
1442 | | - props.put("mail.smtp.starttls.enable", "true"); |
1443 | | - props.put("mail.smtp.host", host); |
1444 | | - props.put("mail.smtp.port", port); |
1445 | | - |
1446 | | - Session session = Session.getInstance(props, new Authenticator() { |
1447 | | - protected PasswordAuthentication getPasswordAuthentication() { |
1448 | | - return new PasswordAuthentication(user, pass); |
1449 | | - } |
1450 | | - }); |
1451 | 1461 |
|
1452 | | - Message message = new MimeMessage(session); |
1453 | | - message.setFrom(new InternetAddress(from)); |
1454 | | - message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to)); |
1455 | | - message.setSubject(subject); |
1456 | | - message.setText(body); |
| 1462 | +// public static boolean verifyActivationCode(String userId, String code) throws Exception { |
| 1463 | +// MongoCollection<DataModel.MongoActivation> col = DUUIMongoDBStorage.Activations(); |
| 1464 | +// String codeHash = hashCode(code); |
| 1465 | +// Instant now = Instant.now(); |
| 1466 | +// |
| 1467 | +// var filter = Filters.and( |
| 1468 | +// Filters.eq("userId", userId), |
| 1469 | +// Filters.eq("purpose", "activation"), |
| 1470 | +// Filters.eq("codeHash", codeHash), |
| 1471 | +// Filters.eq("used", false), |
| 1472 | +// Filters.gte("expiresAt", now) |
| 1473 | +// ); |
| 1474 | +// |
| 1475 | +// var update = Updates.combine( |
| 1476 | +// Updates.set("used", true) |
| 1477 | +// ); |
| 1478 | +// |
| 1479 | +// var res = col.findOneAndUpdate(filter, update, new FindOneAndUpdateOptions() |
| 1480 | +// .returnDocument(ReturnDocument.AFTER)); |
| 1481 | +// |
| 1482 | +// if (res != null) { |
| 1483 | +// DUUIMongoDBStorage |
| 1484 | +// .Users() |
| 1485 | +// .findOneAndUpdate( |
| 1486 | +// Filters.eq(userId), |
| 1487 | +// Updates.set("activated", true) |
| 1488 | +// ); |
| 1489 | +// } |
| 1490 | +// |
| 1491 | +// |
| 1492 | +// return res != null; // true => success; false => invalid/expired/already used |
| 1493 | +// } |
1457 | 1494 |
|
1458 | | - Transport.send(message); |
| 1495 | + public static String issueRecoveryCode(String userId, Duration ttl) throws Exception { |
| 1496 | + MongoCollection<DataModel.MongoActivation> col = DUUIMongoDBStorage.Activations(); |
| 1497 | + String code = sixDigit(); |
| 1498 | + String codeHash = hashCode(code); |
| 1499 | + Instant now = Instant.now(); |
| 1500 | + Instant exp = now.plus(ttl); |
| 1501 | + |
| 1502 | + // upsert: replace any previous unused code for this user/purpose |
| 1503 | + var filter = Filters.and(Filters.eq("userId", userId), |
| 1504 | + Filters.eq("purpose", "recovery"), |
| 1505 | + Filters.eq("used", false)); |
| 1506 | + var rec = new DataModel.MongoActivation(null, userId, "recovery", codeHash, exp, now, false); |
| 1507 | + var options = new FindOneAndReplaceOptions().upsert(true).returnDocument(ReturnDocument.AFTER); |
| 1508 | + col.findOneAndReplace(filter, rec, options); |
| 1509 | + |
| 1510 | + return code; // send this in the email |
| 1511 | + } |
| 1512 | + |
| 1513 | + public static boolean verifyRecoveryCode(String userId, String code) throws Exception { |
| 1514 | + MongoCollection<DataModel.MongoActivation> col = DUUIMongoDBStorage.Activations(); |
| 1515 | + String codeHash = hashCode(code); |
| 1516 | + Instant now = Instant.now(); |
| 1517 | + |
| 1518 | + var filter = Filters.and( |
| 1519 | + Filters.eq("userId", userId), |
| 1520 | + Filters.eq("purpose", "recovery"), |
| 1521 | + Filters.eq("codeHash", codeHash), |
| 1522 | + Filters.eq("used", false), |
| 1523 | + Filters.gte("expiresAt", now) |
| 1524 | + ); |
| 1525 | + |
| 1526 | + var update = Updates.combine( |
| 1527 | + Updates.set("used", true) |
| 1528 | + ); |
| 1529 | + |
| 1530 | + var res = col.findOneAndUpdate(filter, update, new FindOneAndUpdateOptions() |
| 1531 | + .returnDocument(ReturnDocument.AFTER)); |
| 1532 | + |
| 1533 | + return res != null; // true => success; false => invalid/expired/already used |
1459 | 1534 | } |
1460 | 1535 |
|
1461 | 1536 |
|
|
0 commit comments