mirror of
https://github.com/theonedev/onedev.git
synced 2025-12-08 18:26:30 +00:00
Fix issue #645 - Verify user emails
This commit is contained in:
parent
2c0148003b
commit
0f8bc2b958
2
pom.xml
2
pom.xml
@ -362,7 +362,7 @@
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.67</version>
|
||||
<version>1.69</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.unbescape</groupId>
|
||||
|
||||
@ -100,6 +100,7 @@ import io.onedev.server.entitymanager.CodeCommentManager;
|
||||
import io.onedev.server.entitymanager.CodeCommentQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.CodeCommentReplyManager;
|
||||
import io.onedev.server.entitymanager.CommitQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.GitLfsLockManager;
|
||||
import io.onedev.server.entitymanager.GroupAuthorizationManager;
|
||||
import io.onedev.server.entitymanager.GroupManager;
|
||||
@ -143,6 +144,7 @@ import io.onedev.server.entitymanager.impl.DefaultCodeCommentManager;
|
||||
import io.onedev.server.entitymanager.impl.DefaultCodeCommentQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.impl.DefaultCodeCommentReplyManager;
|
||||
import io.onedev.server.entitymanager.impl.DefaultCommitQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.impl.DefaultEmailAddressManager;
|
||||
import io.onedev.server.entitymanager.impl.DefaultGitLfsLockManager;
|
||||
import io.onedev.server.entitymanager.impl.DefaultGroupAuthorizationManager;
|
||||
import io.onedev.server.entitymanager.impl.DefaultGroupManager;
|
||||
@ -432,6 +434,7 @@ public class CoreModule extends AbstractPluginModule {
|
||||
bind(LinkSpecManager.class).to(DefaultLinkSpecManager.class);
|
||||
bind(IssueLinkManager.class).to(DefaultIssueLinkManager.class);
|
||||
bind(LinkAuthorizationManager.class).to(DefaultLinkAuthorizationManager.class);
|
||||
bind(EmailAddressManager.class).to(DefaultEmailAddressManager.class);
|
||||
|
||||
bind(WebHookManager.class);
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import io.onedev.server.buildspec.job.action.notificationreceiver.NotificationRe
|
||||
import io.onedev.server.entitymanager.GroupManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Group;
|
||||
import io.onedev.server.model.User;
|
||||
|
||||
@ -37,7 +38,7 @@ public class NotificationReceiver {
|
||||
}
|
||||
|
||||
public static NotificationReceiver parse(String receiverString, @Nullable Build build) {
|
||||
Collection<String> emails = new HashSet<>();
|
||||
Collection<String> emailAddresses = new HashSet<>();
|
||||
|
||||
CharStream is = CharStreams.fromString(receiverString);
|
||||
NotificationReceiverLexer lexer = new NotificationReceiverLexer(is);
|
||||
@ -61,22 +62,27 @@ public class NotificationReceiver {
|
||||
String userName = getValue(criteria.userCriteria().Value());
|
||||
User user = OneDev.getInstance(UserManager.class).findByName(userName);
|
||||
if (user != null)
|
||||
emails.add(user.getEmail());
|
||||
addEmailAddress(emailAddresses, user);
|
||||
else
|
||||
throw new ExplicitException("Unable to find user '" + userName + "'");
|
||||
} else if (criteria.groupCriteria() != null) {
|
||||
String groupName = getValue(criteria.groupCriteria().Value());
|
||||
Group group = OneDev.getInstance(GroupManager.class).find(groupName);
|
||||
if (group != null)
|
||||
emails.addAll(group.getMembers().stream().map(it->it.getEmail()).collect(Collectors.toList()));
|
||||
else
|
||||
if (group != null) {
|
||||
emailAddresses.addAll(group.getMembers().stream()
|
||||
.map(it->it.getPrimaryEmailAddress())
|
||||
.filter(it-> it!=null && it.isVerified())
|
||||
.map(it->it.getValue())
|
||||
.collect(Collectors.toList()));
|
||||
} else {
|
||||
throw new ExplicitException("Unable to find group '" + groupName + "'");
|
||||
}
|
||||
} else if (criteria.Committers() != null) {
|
||||
if (build != null) {
|
||||
for (RevCommit commit: build.getCommits(null)) {
|
||||
PersonIdent committer = commit.getCommitterIdent();
|
||||
if (committer != null && committer.getEmailAddress() != null)
|
||||
emails.add(committer.getEmailAddress());
|
||||
emailAddresses.add(committer.getEmailAddress());
|
||||
}
|
||||
}
|
||||
} else if (criteria.Authors() != null) {
|
||||
@ -84,7 +90,7 @@ public class NotificationReceiver {
|
||||
for (RevCommit commit: build.getCommits(null)) {
|
||||
PersonIdent author = commit.getAuthorIdent();
|
||||
if (author != null && author.getEmailAddress() != null)
|
||||
emails.add(author.getEmailAddress());
|
||||
emailAddresses.add(author.getEmailAddress());
|
||||
}
|
||||
}
|
||||
} else if (criteria.CommittersSincePreviousSuccessful() != null) {
|
||||
@ -92,7 +98,7 @@ public class NotificationReceiver {
|
||||
for (RevCommit commit: build.getCommits(Build.Status.SUCCESSFUL)) {
|
||||
PersonIdent committer = commit.getCommitterIdent();
|
||||
if (committer != null && committer.getEmailAddress() != null)
|
||||
emails.add(committer.getEmailAddress());
|
||||
emailAddresses.add(committer.getEmailAddress());
|
||||
}
|
||||
}
|
||||
} else if (criteria.AuthorsSincePreviousSuccessful() != null) {
|
||||
@ -100,18 +106,24 @@ public class NotificationReceiver {
|
||||
for (RevCommit commit: build.getCommits(Build.Status.SUCCESSFUL)) {
|
||||
PersonIdent author = commit.getAuthorIdent();
|
||||
if (author != null && author.getEmailAddress() != null)
|
||||
emails.add(author.getEmailAddress());
|
||||
emailAddresses.add(author.getEmailAddress());
|
||||
}
|
||||
}
|
||||
} else if (criteria.Submitter() != null) {
|
||||
if (build != null && build.getSubmitter() != null)
|
||||
emails.add(build.getSubmitter().getEmail());
|
||||
addEmailAddress(emailAddresses, build.getSubmitter());
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected notification receiver criteria");
|
||||
}
|
||||
}
|
||||
|
||||
return new NotificationReceiver(emails);
|
||||
return new NotificationReceiver(emailAddresses);
|
||||
}
|
||||
|
||||
private static void addEmailAddress(Collection<String> emailAddressValues, User user) {
|
||||
EmailAddress emailAddress = user.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
emailAddressValues.add(emailAddress.getValue());
|
||||
}
|
||||
|
||||
private static String getValue(TerminalNode terminal) {
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package io.onedev.server.entitymanager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
|
||||
public interface EmailAddressManager extends EntityManager<EmailAddress> {
|
||||
|
||||
@Nullable
|
||||
EmailAddress findByValue(String value);
|
||||
|
||||
@Nullable
|
||||
EmailAddress findByPersonIdent(PersonIdent personIdent);
|
||||
|
||||
void setAsPrimary(EmailAddress emailAddress);
|
||||
|
||||
void useForGitOperations(EmailAddress emailAddress);
|
||||
|
||||
void sendVerificationEmail(EmailAddress emailAddress);
|
||||
|
||||
}
|
||||
@ -5,12 +5,8 @@ import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.SsoInfo;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
|
||||
public interface UserManager extends EntityManager<User> {
|
||||
|
||||
@ -69,42 +65,15 @@ public interface UserManager extends EntityManager<User> {
|
||||
@Nullable
|
||||
User findByFullName(String fullName);
|
||||
|
||||
@Nullable
|
||||
User findBySsoInfo(SsoInfo ssoInfo);
|
||||
|
||||
@Nullable
|
||||
User findByAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* Find user of specified name.
|
||||
* <p>
|
||||
* @param userName
|
||||
* name of the user
|
||||
* @return
|
||||
* matching user, or <tt>null</tt> if not found
|
||||
*/
|
||||
@Nullable
|
||||
User findByEmail(String email);
|
||||
|
||||
@Nullable
|
||||
UserFacade findFacadeByEmail(String email);
|
||||
User findByVerifiedEmailAddress(String emailAddress);
|
||||
|
||||
/**
|
||||
* Find user by person
|
||||
* <p>
|
||||
* @param person
|
||||
* Git person representation
|
||||
* @return
|
||||
* matching user, or <tt>null</tt> if not found
|
||||
*/
|
||||
@Nullable
|
||||
User find(PersonIdent person);
|
||||
List<User> query(@Nullable String term, int firstResult, int maxResults);
|
||||
|
||||
@Nullable
|
||||
UserFacade findFacade(PersonIdent person);
|
||||
|
||||
@Nullable
|
||||
UserFacade getFacade(Long userId);
|
||||
int count(String term);
|
||||
|
||||
List<User> queryAndSort(Collection<User> topUsers);
|
||||
|
||||
|
||||
@ -0,0 +1,223 @@
|
||||
package io.onedev.server.entitymanager.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.commons.loader.Listen;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.notification.MailManager;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.BaseEntityManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
|
||||
@Singleton
|
||||
public class DefaultEmailAddressManager extends BaseEntityManager<EmailAddress> implements EmailAddressManager {
|
||||
|
||||
private final SettingManager settingManager;
|
||||
|
||||
private final MailManager mailManager;
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final SessionManager sessionManager;
|
||||
|
||||
private final Map<String, Long> idCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
public DefaultEmailAddressManager(Dao dao, SettingManager settingManager, MailManager mailManager,
|
||||
TransactionManager transactionManager, SessionManager sessionManager) {
|
||||
super(dao);
|
||||
this.settingManager = settingManager;
|
||||
this.mailManager = mailManager;
|
||||
this.transactionManager = transactionManager;
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
@Listen
|
||||
@Sessional
|
||||
public void on(SystemStarted event) {
|
||||
for (EmailAddress address: query())
|
||||
idCache.put(address.getValue(), address.getId());
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public EmailAddress findByValue(String value) {
|
||||
Long id = idCache.get(value.toLowerCase());
|
||||
return id != null? load(id): null;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public EmailAddress findByPersonIdent(PersonIdent personIdent) {
|
||||
if (StringUtils.isNotBlank(personIdent.getEmailAddress()))
|
||||
return findByValue(personIdent.getEmailAddress());
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void setAsPrimary(EmailAddress emailAddress) {
|
||||
for (EmailAddress each: emailAddress.getOwner().getEmailAddresses())
|
||||
each.setPrimary(false);
|
||||
emailAddress.setPrimary(true);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void useForGitOperations(EmailAddress emailAddress) {
|
||||
for (EmailAddress each: emailAddress.getOwner().getEmailAddresses())
|
||||
each.setGit(false);
|
||||
emailAddress.setGit(true);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void delete(EmailAddress emailAddress) {
|
||||
super.delete(emailAddress);
|
||||
|
||||
User user = emailAddress.getOwner();
|
||||
|
||||
user.getEmailAddresses().remove(emailAddress);
|
||||
if (!user.getSortedEmailAddresses().isEmpty()) {
|
||||
if (user.getPrimaryEmailAddress() == null)
|
||||
user.getSortedEmailAddresses().iterator().next().setPrimary(true);
|
||||
if (user.getGitEmailAddress() == null)
|
||||
user.getSortedEmailAddresses().iterator().next().setGit(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void save(EmailAddress emailAddress) {
|
||||
boolean isNew = emailAddress.isNew();
|
||||
emailAddress.setValue(emailAddress.getValue().toLowerCase());
|
||||
|
||||
User user = emailAddress.getOwner();
|
||||
if (user.getEmailAddresses().isEmpty()) {
|
||||
emailAddress.setPrimary(true);
|
||||
emailAddress.setGit(true);
|
||||
}
|
||||
super.save(emailAddress);
|
||||
|
||||
user.getEmailAddresses().add(emailAddress);
|
||||
|
||||
if (isNew && settingManager.getMailSetting() != null && !emailAddress.isVerified()) {
|
||||
Long addressId = emailAddress.getId();
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sessionManager.runAsync(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sendVerificationEmail(load(addressId));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(EntityRemoved event) {
|
||||
if (event.getEntity() instanceof EmailAddress) {
|
||||
String value = ((EmailAddress)event.getEntity()).getValue();
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
idCache.remove(value);
|
||||
}
|
||||
|
||||
});
|
||||
} else if (event.getEntity() instanceof User) {
|
||||
User user = (User) event.getEntity();
|
||||
Collection<String> values = user.getEmailAddresses().stream()
|
||||
.map(it->it.getValue()).collect(Collectors.toList());
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
idCache.keySet().removeAll(values);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(EntityPersisted event) {
|
||||
if (event.getEntity() instanceof EmailAddress) {
|
||||
EmailAddress emailAddress = (EmailAddress) event.getEntity();
|
||||
String value = emailAddress.getValue();
|
||||
Long id = emailAddress.getId();
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
idCache.put(value, id);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendVerificationEmail(EmailAddress emailAddress) {
|
||||
Preconditions.checkState(settingManager.getMailSetting() != null
|
||||
&& !emailAddress.isVerified());
|
||||
|
||||
User user = emailAddress.getOwner();
|
||||
|
||||
String serverUrl = settingManager.getSystemSetting().getServerUrl();
|
||||
|
||||
String verificationUrl = String.format("%s/verify-email-address/%d/%s",
|
||||
serverUrl, emailAddress.getId(), emailAddress.getVerficationCode());
|
||||
String htmlBody = String.format("Hello,"
|
||||
+ "<p style='margin: 16px 0;'>"
|
||||
+ "The account \"%s\" at \"%s\" tries to use email address '%s', please visit below link to verify if this is you:<br><br>"
|
||||
+ "<a href='%s'>%s</a>",
|
||||
user.getName(), serverUrl, emailAddress.getValue(), verificationUrl, verificationUrl);
|
||||
|
||||
String textBody = String.format("Hello,\n\n"
|
||||
+ "The account \"%s\" at \"%s\" tries to use email address \"%s\", please visit below link to verify if this is you:\n\n"
|
||||
+ "%s",
|
||||
user.getName(), serverUrl, emailAddress.getValue(), verificationUrl);
|
||||
|
||||
mailManager.sendMail(
|
||||
settingManager.getMailSetting(),
|
||||
Arrays.asList(emailAddress.getValue()),
|
||||
Lists.newArrayList(), Lists.newArrayList(),
|
||||
"[Verification] Please Verify Your Email Address",
|
||||
htmlBody, textBody, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -296,6 +296,7 @@ public class DefaultPullRequestManager extends BaseEntityManager<PullRequest> im
|
||||
ObjectId mergeCommitId = ObjectId.fromString(
|
||||
Preconditions.checkNotNull(mergePreview.getMergeCommitHash()));
|
||||
PersonIdent user = SecurityUtils.getUser().asPerson();
|
||||
|
||||
Project project = request.getTargetProject();
|
||||
MergeStrategy mergeStrategy = mergePreview.getMergeStrategy();
|
||||
|
||||
|
||||
@ -1,36 +1,40 @@
|
||||
package io.onedev.server.entitymanager.impl;
|
||||
|
||||
import static io.onedev.server.model.User.PROP_PASSWORD;
|
||||
import static io.onedev.server.model.User.PROP_SSO_INFO;
|
||||
import static io.onedev.server.model.support.SsoInfo.PROP_CONNECTOR;
|
||||
import static io.onedev.server.model.support.SsoInfo.PROP_SUBJECT;
|
||||
import static io.onedev.server.model.User.PROP_SSO_CONNECTOR;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Subquery;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.hibernate.ReplicationMode;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import io.onedev.commons.loader.Listen;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.IssueFieldManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.model.AbstractEntity;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.BranchProtection;
|
||||
import io.onedev.server.model.support.SsoInfo;
|
||||
import io.onedev.server.model.support.TagProtection;
|
||||
import io.onedev.server.persistence.IdManager;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
@ -39,7 +43,6 @@ import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.BaseEntityManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
import io.onedev.server.util.usage.Usage;
|
||||
|
||||
@Singleton
|
||||
@ -53,48 +56,26 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
|
||||
private final IdManager idManager;
|
||||
|
||||
private final EmailAddressManager emailAddressManager;
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final Map<String, Long> userIdByEmail = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<Long, UserFacade> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Long> idCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
public DefaultUserManager(Dao dao, ProjectManager projectManager, SettingManager settingManager,
|
||||
IssueFieldManager issueFieldManager, IdManager idManager, TransactionManager transactionManager) {
|
||||
IssueFieldManager issueFieldManager, IdManager idManager,
|
||||
EmailAddressManager emailAddressManager, TransactionManager transactionManager) {
|
||||
super(dao);
|
||||
|
||||
this.projectManager = projectManager;
|
||||
this.settingManager = settingManager;
|
||||
this.issueFieldManager = issueFieldManager;
|
||||
this.idManager = idManager;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(SystemStarted event) {
|
||||
String queryString = String.format("select id, %s, %s, %s, %s, %s from User",
|
||||
User.PROP_NAME, User.PROP_FULL_NAME, User.PROP_EMAIL, User.PROP_GIT_EMAIL, User.PROP_ALTERNATE_EMAILS);
|
||||
Query<?> query = dao.getSession().createQuery(queryString);
|
||||
for (Object[] fields: (List<Object[]>)query.list()) {
|
||||
Long userId = (Long) fields[0];
|
||||
String name = (String) fields[1];
|
||||
String fullName = (String) fields[2];
|
||||
String email = (String) fields[3];
|
||||
String gitEmail = (String) fields[4];
|
||||
List<String> alternateEmails = (List<String>) fields[5];
|
||||
|
||||
userIdByEmail.put(email, userId);
|
||||
if (gitEmail != null)
|
||||
userIdByEmail.put(gitEmail, userId);
|
||||
for (String alternateEmail: alternateEmails)
|
||||
userIdByEmail.put(alternateEmail, userId);
|
||||
cache.put(userId, new UserFacade(userId, name, fullName, email, gitEmail, alternateEmails));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void replicate(User user) {
|
||||
@ -105,6 +86,8 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
@Transactional
|
||||
@Override
|
||||
public void save(User user, String oldName) {
|
||||
user.setName(user.getName().toLowerCase());
|
||||
|
||||
dao.persist(user);
|
||||
|
||||
if (oldName != null && !oldName.equals(user.getName())) {
|
||||
@ -120,20 +103,6 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
|
||||
issueFieldManager.onRenameUser(oldName, user.getName());
|
||||
}
|
||||
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
userIdByEmail.put(user.getEmail(), user.getId());
|
||||
if (user.getGitEmail() != null)
|
||||
userIdByEmail.put(user.getGitEmail(), user.getId());
|
||||
for (String alternateEmail: user.getAlternateEmails())
|
||||
userIdByEmail.put(alternateEmail, user.getId());
|
||||
cache.put(user.getId(), new UserFacade(user));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -249,29 +218,19 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
query.executeUpdate();
|
||||
|
||||
dao.remove(user);
|
||||
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
cache.remove(user.getId());
|
||||
userIdByEmail.remove(user.getEmail());
|
||||
if (user.getGitEmail() != null)
|
||||
userIdByEmail.remove(user.getGitEmail());
|
||||
for (String email: user.getAlternateEmails())
|
||||
userIdByEmail.remove(email);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public User findByName(String userName) {
|
||||
EntityCriteria<User> criteria = newCriteria();
|
||||
criteria.add(Restrictions.ilike(User.PROP_NAME, userName));
|
||||
criteria.setCacheable(true);
|
||||
return find(criteria);
|
||||
userName = userName.toLowerCase();
|
||||
Long id = idCache.get(userName);
|
||||
if (id != null) {
|
||||
User user = get(id);
|
||||
if (user != null && user.getName().equals(userName))
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@ -292,16 +251,6 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
return find(criteria);
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public User findBySsoInfo(SsoInfo ssoInfo) {
|
||||
EntityCriteria<User> criteria = newCriteria();
|
||||
criteria.add(Restrictions.eq(User.PROP_SSO_INFO + "." + SsoInfo.PROP_CONNECTOR, ssoInfo.getConnector()));
|
||||
criteria.add(Restrictions.eq(User.PROP_SSO_INFO + "." + SsoInfo.PROP_SUBJECT, ssoInfo.getSubject()));
|
||||
criteria.setCacheable(true);
|
||||
return find(criteria);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> query() {
|
||||
EntityCriteria<User> criteria = newCriteria();
|
||||
@ -315,47 +264,6 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
return count(true);
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public UserFacade findFacadeByEmail(String email) {
|
||||
Long userId = userIdByEmail.get(email);
|
||||
if (userId != null) {
|
||||
UserFacade user = cache.get(userId);
|
||||
if (user != null && user.isUsingEmail(email))
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public UserFacade findFacade(PersonIdent person) {
|
||||
if (StringUtils.isNotBlank(person.getEmailAddress()))
|
||||
return findFacadeByEmail(person.getEmailAddress());
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public UserFacade getFacade(Long userId) {
|
||||
return cache.get(userId);
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public User findByEmail(String email) {
|
||||
UserFacade facade = findFacadeByEmail(email);
|
||||
return facade!=null? load(facade.getId()): null;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public User find(PersonIdent person) {
|
||||
UserFacade facade = findFacade(person);
|
||||
return facade!=null? load(facade.getId()): null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> queryAndSort(Collection<User> topUsers) {
|
||||
List<User> users = query();
|
||||
@ -365,12 +273,53 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
return users;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(SystemStarted event) {
|
||||
for (User user: query())
|
||||
idCache.put(user.getName(), user.getId());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(EntityRemoved event) {
|
||||
if (event.getEntity() instanceof User) {
|
||||
User user = (User) event.getEntity();
|
||||
String name = user.getName();
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
idCache.remove(name);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(EntityPersisted event) {
|
||||
if (event.getEntity() instanceof User) {
|
||||
User user = (User) event.getEntity();
|
||||
String name = user.getName();
|
||||
Long id = user.getId();
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
idCache.put(name, id);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void onRenameSsoConnector(String oldName, String newName) {
|
||||
String connectorProp = PROP_SSO_INFO + "." + PROP_CONNECTOR;
|
||||
Query<?> query = getSession().createQuery(String.format("update User set %s=:newName "
|
||||
+ "where %s=:oldName", connectorProp, connectorProp));
|
||||
+ "where %s=:oldName", oldName, newName));
|
||||
query.setParameter("oldName", oldName);
|
||||
query.setParameter("newName", newName);
|
||||
query.executeUpdate();
|
||||
@ -379,13 +328,70 @@ public class DefaultUserManager extends BaseEntityManager<User> implements UserM
|
||||
@Transactional
|
||||
@Override
|
||||
public void onDeleteSsoConnector(String name) {
|
||||
String connectorProp = PROP_SSO_INFO + "." + PROP_CONNECTOR;
|
||||
String subjectProp = PROP_SSO_INFO + "." + PROP_SUBJECT;
|
||||
Query<?> query = getSession().createQuery(String.format("update User set %s=null, %s='%s', %s='12345' "
|
||||
Query<?> query = getSession().createQuery(String.format("update User set %s=null, %s='12345' "
|
||||
+ "where %s=:name",
|
||||
connectorProp, subjectProp, UUID.randomUUID().toString(), PROP_PASSWORD, connectorProp));
|
||||
PROP_SSO_CONNECTOR, PROP_PASSWORD, PROP_SSO_CONNECTOR));
|
||||
query.setParameter("name", name);
|
||||
query.executeUpdate();
|
||||
}
|
||||
|
||||
private Predicate[] getPredicates(CriteriaBuilder builder, CriteriaQuery<?> query,
|
||||
Root<User> root, String term) {
|
||||
if (term != null) {
|
||||
term = "%" + term.toLowerCase() + "%";
|
||||
|
||||
Subquery<EmailAddress> addressQuery = query.subquery(EmailAddress.class);
|
||||
Root<EmailAddress> addressRoot = addressQuery.from(EmailAddress.class);
|
||||
addressQuery.select(addressRoot);
|
||||
|
||||
Predicate ownerPredicate = builder.equal(addressRoot.get(EmailAddress.PROP_OWNER), root);
|
||||
Predicate valuePredicate = builder.like(addressRoot.get(EmailAddress.PROP_VALUE), term);
|
||||
return new Predicate[] {
|
||||
builder.gt(root.get(AbstractEntity.PROP_ID), 0),
|
||||
builder.or(
|
||||
builder.like(root.get(User.PROP_NAME), term),
|
||||
builder.like(builder.lower(root.get(User.PROP_FULL_NAME)), term),
|
||||
builder.exists(addressQuery.where(ownerPredicate, valuePredicate)))};
|
||||
} else {
|
||||
return new Predicate[] {builder.gt(root.get(AbstractEntity.PROP_ID), 0)};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> query(String term, int firstResult, int maxResults) {
|
||||
CriteriaBuilder builder = getSession().getCriteriaBuilder();
|
||||
CriteriaQuery<User> criteriaQuery = builder.createQuery(User.class);
|
||||
Root<User> root = criteriaQuery.from(User.class);
|
||||
|
||||
criteriaQuery.where(getPredicates(builder, criteriaQuery, root, term));
|
||||
|
||||
Query<User> query = getSession().createQuery(criteriaQuery);
|
||||
query.setFirstResult(firstResult);
|
||||
query.setMaxResults(maxResults);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count(String term) {
|
||||
CriteriaBuilder builder = getSession().getCriteriaBuilder();
|
||||
CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
|
||||
Root<User> root = criteriaQuery.from(User.class);
|
||||
|
||||
criteriaQuery.select(builder.count(root));
|
||||
criteriaQuery.where(getPredicates(builder, criteriaQuery, root, term));
|
||||
|
||||
return getSession().createQuery(criteriaQuery).uniqueResult().intValue();
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public User findByVerifiedEmailAddress(String emailAddressValue) {
|
||||
EmailAddress emailAddress = emailAddressManager.findByValue(emailAddressValue);
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
return emailAddress.getOwner();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,7 +4,7 @@ import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.model.PullRequestUpdate;
|
||||
import io.onedev.server.model.User;
|
||||
|
||||
@ -32,8 +32,9 @@ public class PullRequestUpdated extends PullRequestEvent {
|
||||
if (committers == null) {
|
||||
committers = getUpdate().getCommits()
|
||||
.stream()
|
||||
.map(it->OneDev.getInstance(UserManager.class).find(it.getCommitterIdent()))
|
||||
.filter(it->it!=null)
|
||||
.map(it->OneDev.getInstance(EmailAddressManager.class).findByPersonIdent(it.getCommitterIdent()))
|
||||
.filter(it -> it!=null && it.isVerified())
|
||||
.map(it->it.getOwner())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
return committers;
|
||||
|
||||
@ -40,14 +40,13 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.commons.loader.Listen;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.commons.utils.PathUtils;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.event.RefUpdated;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
@ -60,6 +59,7 @@ import io.onedev.server.git.command.ListNumStatsCommand;
|
||||
import io.onedev.server.git.command.LogCommand;
|
||||
import io.onedev.server.git.command.RevListCommand;
|
||||
import io.onedev.server.git.command.RevListCommand.Order;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
@ -73,7 +73,6 @@ import io.onedev.server.util.Pair;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.concurrent.BatchWorker;
|
||||
import io.onedev.server.util.concurrent.Prioritized;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
import jetbrains.exodus.ArrayByteIterable;
|
||||
import jetbrains.exodus.ByteIterable;
|
||||
import jetbrains.exodus.backup.BackupStrategy;
|
||||
@ -286,7 +285,7 @@ public class DefaultCommitInfoManager extends AbstractMultiEnvironmentManager im
|
||||
|
||||
private final SessionManager sessionManager;
|
||||
|
||||
private final UserManager userManager;
|
||||
private final EmailAddressManager emailAddressManager;
|
||||
|
||||
private final Map<Long, List<String>> filesCache = new ConcurrentHashMap<>();
|
||||
|
||||
@ -296,12 +295,13 @@ public class DefaultCommitInfoManager extends AbstractMultiEnvironmentManager im
|
||||
|
||||
@Inject
|
||||
public DefaultCommitInfoManager(ProjectManager projectManager, StorageManager storageManager,
|
||||
BatchWorkManager batchWorkManager, SessionManager sessionManager, UserManager userManager) {
|
||||
BatchWorkManager batchWorkManager, SessionManager sessionManager,
|
||||
EmailAddressManager emailAddressManager) {
|
||||
this.projectManager = projectManager;
|
||||
this.storageManager = storageManager;
|
||||
this.batchWorkManager = batchWorkManager;
|
||||
this.sessionManager = sessionManager;
|
||||
this.userManager = userManager;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
}
|
||||
|
||||
private boolean isCommitCollected(byte[] commitBytes) {
|
||||
@ -1016,26 +1016,23 @@ public class DefaultCommitInfoManager extends AbstractMultiEnvironmentManager im
|
||||
Store emailToIndexStore = getStore(env, USER_TO_INDEX_STORE);
|
||||
Store pathToIndexStore = getStore(env, PATH_TO_INDEX_STORE);
|
||||
Store commitCountStore = getStore(env, COMMIT_COUNTS_STORE);
|
||||
|
||||
return env.computeInReadonlyTransaction(new TransactionalComputable<Integer>() {
|
||||
|
||||
@Override
|
||||
public Integer compute(Transaction txn) {
|
||||
Collection<String> emails = Sets.newHashSet(user.getEmail());
|
||||
if (user.getGitEmail() != null)
|
||||
emails.add(user.getGitEmail());
|
||||
emails.addAll(user.getAlternateEmails());
|
||||
int count = 0;
|
||||
for (String email: emails) {
|
||||
int userIndex = readInt(emailToIndexStore, txn, new StringByteIterable(email), -1);
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
user.getEmailAddresses().stream().filter(it->it.isVerified()).forEach(it-> {
|
||||
int userIndex = readInt(emailToIndexStore, txn, new StringByteIterable(it.getValue()), -1);
|
||||
if (userIndex != -1) {
|
||||
int pathIndex = readInt(pathToIndexStore, txn, new StringByteIterable(path), -1);
|
||||
if (pathIndex != -1) {
|
||||
long commitCountKey = (userIndex<<32)|pathIndex;
|
||||
count += readInt(commitCountStore, txn, new LongByteIterable(commitCountKey), 0);
|
||||
count.addAndGet(readInt(commitCountStore, txn, new LongByteIterable(commitCountKey), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
});
|
||||
return count.get();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1341,9 +1338,15 @@ public class DefaultCommitInfoManager extends AbstractMultiEnvironmentManager im
|
||||
byte[] userBytes = readBytes(indexToUserStore, txn, new IntByteIterable(userIndex));
|
||||
if (userBytes != null) {
|
||||
NameAndEmail user = (NameAndEmail) SerializationUtils.deserialize(userBytes);
|
||||
UserFacade facade = userManager.findFacadeByEmail(user.getEmailAddress());
|
||||
if (facade != null)
|
||||
user = facade.getNameAndEmail();
|
||||
EmailAddress emailAddress = emailAddressManager.findByValue(user.getEmailAddress());
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
User owner = emailAddress.getOwner();
|
||||
EmailAddress primaryEmailAddress = owner.getPrimaryEmailAddress();
|
||||
if (primaryEmailAddress != null && primaryEmailAddress.isVerified())
|
||||
user = new NameAndEmail(owner.getDisplayName(), primaryEmailAddress.getValue());
|
||||
else
|
||||
user = new NameAndEmail(owner.getDisplayName(), emailAddress.getValue());
|
||||
}
|
||||
userOpt = Optional.of(user);
|
||||
} else {
|
||||
userOpt = Optional.empty();
|
||||
|
||||
@ -8,7 +8,6 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@ -30,11 +29,13 @@ import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.commons.utils.ExceptionUtils;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.LinkSpecManager;
|
||||
import io.onedev.server.entitymanager.RoleManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.event.system.SystemStarting;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.LinkSpec;
|
||||
import io.onedev.server.model.Role;
|
||||
import io.onedev.server.model.Setting;
|
||||
@ -63,6 +64,7 @@ import io.onedev.server.util.init.ManualConfig;
|
||||
import io.onedev.server.util.init.Skippable;
|
||||
import io.onedev.server.util.schedule.SchedulableTask;
|
||||
import io.onedev.server.util.schedule.TaskScheduler;
|
||||
import io.onedev.server.web.util.NewUserBean;
|
||||
|
||||
@Singleton
|
||||
public class DefaultDataManager implements DataManager, Serializable {
|
||||
@ -85,6 +87,8 @@ public class DefaultDataManager implements DataManager, Serializable {
|
||||
|
||||
private final LinkSpecManager linkSpecManager;
|
||||
|
||||
private final EmailAddressManager emailAddressManager;
|
||||
|
||||
private String backupTaskId;
|
||||
|
||||
@Inject
|
||||
@ -92,7 +96,7 @@ public class DefaultDataManager implements DataManager, Serializable {
|
||||
SettingManager settingManager, PersistManager persistManager,
|
||||
MailManager mailManager, Validator validator, TaskScheduler taskScheduler,
|
||||
PasswordService passwordService, RoleManager roleManager,
|
||||
LinkSpecManager linkSpecManager) {
|
||||
LinkSpecManager linkSpecManager, EmailAddressManager emailAddressManager) {
|
||||
this.userManager = userManager;
|
||||
this.settingManager = settingManager;
|
||||
this.validator = validator;
|
||||
@ -102,6 +106,7 @@ public class DefaultDataManager implements DataManager, Serializable {
|
||||
this.passwordService = passwordService;
|
||||
this.roleManager = roleManager;
|
||||
this.linkSpecManager = linkSpecManager;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"serial"})
|
||||
@ -113,8 +118,8 @@ public class DefaultDataManager implements DataManager, Serializable {
|
||||
if (system == null) {
|
||||
system = new User();
|
||||
system.setId(User.SYSTEM_ID);
|
||||
system.setName(User.SYSTEM_NAME);
|
||||
system.setEmail("system email");
|
||||
system.setName(User.SYSTEM_NAME.toLowerCase());
|
||||
system.setFullName(User.SYSTEM_NAME);
|
||||
system.setPassword("no password");
|
||||
userManager.replicate(system);
|
||||
}
|
||||
@ -122,17 +127,13 @@ public class DefaultDataManager implements DataManager, Serializable {
|
||||
if (unknown == null) {
|
||||
unknown = new User();
|
||||
unknown.setId(User.UNKNOWN_ID);
|
||||
unknown.setName(User.UNKNOWN_NAME);
|
||||
unknown.setEmail("unknown email");
|
||||
unknown.setName(User.UNKNOWN_NAME.toLowerCase());
|
||||
unknown.setFullName(User.UNKNOWN_NAME);
|
||||
unknown.setPassword("no password");
|
||||
userManager.replicate(unknown);
|
||||
}
|
||||
User administrator = userManager.get(User.ROOT_ID);
|
||||
if (administrator == null) {
|
||||
administrator = new User();
|
||||
administrator.setId(User.ROOT_ID);
|
||||
Set<String> excludedProperties = Sets.newHashSet(User.PROP_GIT_EMAIL, User.PROP_ALTERNATE_EMAILS);
|
||||
manualConfigs.add(new ManualConfig("Create Administrator Account", null, administrator, excludedProperties) {
|
||||
if (userManager.get(User.ROOT_ID) == null) {
|
||||
manualConfigs.add(new ManualConfig("Create Administrator Account", null, new NewUserBean()) {
|
||||
|
||||
@Override
|
||||
public Skippable getSkippable() {
|
||||
@ -141,9 +142,20 @@ public class DefaultDataManager implements DataManager, Serializable {
|
||||
|
||||
@Override
|
||||
public void complete() {
|
||||
User user = (User) getSetting();
|
||||
user.setPassword(passwordService.encryptPassword(user.getPassword()));
|
||||
NewUserBean newUserBean = (NewUserBean) getSetting();
|
||||
User user = new User();
|
||||
user.setId(User.ROOT_ID);
|
||||
user.setName(newUserBean.getName());
|
||||
user.setFullName(newUserBean.getFullName());
|
||||
user.setPassword(passwordService.encryptPassword(newUserBean.getPassword()));
|
||||
userManager.replicate(user);
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setValue(newUserBean.getEmailAddress());
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddress.setOwner(user);
|
||||
|
||||
emailAddressManager.save(emailAddress);
|
||||
}
|
||||
|
||||
});
|
||||
@ -400,9 +412,13 @@ public class DefaultDataManager implements DataManager, Serializable {
|
||||
+ "Error detail:\n"
|
||||
+ "%s",
|
||||
url, Throwables.getStackTraceAsString(e));
|
||||
mailManager.sendMail(Lists.newArrayList(root.getEmail()), Lists.newArrayList(),
|
||||
Lists.newArrayList(), "[Backup] OneDev Database Auto-backup Failed",
|
||||
htmlBody, textBody, null, null);
|
||||
|
||||
EmailAddress emailAddress = root.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMail(Lists.newArrayList(emailAddress.getValue()), Lists.newArrayList(),
|
||||
Lists.newArrayList(), "[Backup] OneDev Database Auto-backup Failed",
|
||||
htmlBody, textBody, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public Object writeReplace() throws ObjectStreamException {
|
||||
|
||||
@ -63,8 +63,9 @@ public class ResetAdminPassword extends DefaultPersistManager {
|
||||
System.exit(1);
|
||||
}
|
||||
String password = Bootstrap.command.getArgs()[0];
|
||||
root.setPassword(passwordService.encryptPassword(password));
|
||||
root.setSsoConnector(null);
|
||||
root.setTwoFactorAuthentication(null);
|
||||
root.setPassword(passwordService.encryptPassword(password));
|
||||
userManager.save(root);
|
||||
|
||||
// wait for a short period to have embedded db flushing data
|
||||
|
||||
@ -3796,10 +3796,62 @@ public class DataMigrator {
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate to 6.4.0
|
||||
// Migrate to 7.0.0
|
||||
private void migrate82(File dataDir, Stack<Integer> versions) {
|
||||
Set<String> userNames = new HashSet<>();
|
||||
Map<String, String> primaryEmails = new HashMap<>();
|
||||
Map<String, String> gitEmails = new HashMap<>();
|
||||
Map<String, String> alternateEmails = new HashMap<>();
|
||||
|
||||
for (File file: dataDir.listFiles()) {
|
||||
if (file.getName().startsWith("Builds.xml")) {
|
||||
if (file.getName().startsWith("Users.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element: dom.getRootElement().elements()) {
|
||||
String userId = element.elementText("id").trim();
|
||||
Element nameElement = element.element("name");
|
||||
String name = nameElement.getText().trim();
|
||||
if (userNames.add(name.toLowerCase()))
|
||||
nameElement.setText(name.toLowerCase());
|
||||
else
|
||||
throw new ExplicitException("Duplicated login names found when convert '" + name + "' to lowercase");
|
||||
if (userId.equals("-1")) {
|
||||
element.addElement("fullName").setText("OneDev");
|
||||
element.element("email").detach();
|
||||
element.element("alternateEmails").detach();
|
||||
} else if (userId.equals("-2")) {
|
||||
element.addElement("fullName").setText("Unknown");
|
||||
element.element("email").detach();
|
||||
element.element("alternateEmails").detach();
|
||||
} else {
|
||||
Element emailElement = element.element("email");
|
||||
String email = emailElement.getText().trim();
|
||||
if (primaryEmails.put(email.toLowerCase(), userId) != null)
|
||||
throw new ExplicitException("Duplicated email address found when convert '" + email + "' to lowercase");
|
||||
emailElement.detach();
|
||||
|
||||
Element gitEmailElement = element.element("gitEmail");
|
||||
if (gitEmailElement != null) {
|
||||
String gitEmail = gitEmailElement.getText().trim();
|
||||
gitEmails.put(gitEmail.toLowerCase(), userId);
|
||||
gitEmailElement.detach();
|
||||
}
|
||||
|
||||
Element alternateEmailsElement = element.element("alternateEmails");
|
||||
for (Element alternateEmailElement: alternateEmailsElement.elements()) {
|
||||
String alternateEmail = alternateEmailElement.getText().trim();
|
||||
alternateEmails.put(alternateEmail.toLowerCase(), userId);
|
||||
}
|
||||
alternateEmailsElement.detach();
|
||||
}
|
||||
|
||||
Element ssoInfoElement = element.element("ssoInfo");
|
||||
Element connectorElement = ssoInfoElement.element("connector");
|
||||
if (connectorElement != null)
|
||||
element.addElement("ssoConnector").setText(connectorElement.getText().trim());
|
||||
ssoInfoElement.detach();
|
||||
}
|
||||
dom.writeToFile(file, false);
|
||||
} else if (file.getName().startsWith("Builds.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element: dom.getRootElement().elements())
|
||||
element.element("triggerChain").setName("pipeline");
|
||||
@ -3821,6 +3873,51 @@ public class DataMigrator {
|
||||
dom.writeToFile(file, false);
|
||||
}
|
||||
}
|
||||
|
||||
VersionedXmlDoc emailAddressesDom;
|
||||
File emailAddressesFile = new File(dataDir, "EmailAddresss.xml");
|
||||
emailAddressesDom = new VersionedXmlDoc();
|
||||
Element listElement = emailAddressesDom.addElement("list");
|
||||
|
||||
long id = 1;
|
||||
Map<String, Element> primaryEmailElements = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry: primaryEmails.entrySet()) {
|
||||
Element emailAddressElement = listElement.addElement("io.onedev.server.model.EmailAddress");
|
||||
emailAddressElement.addAttribute("revision", "0.0");
|
||||
emailAddressElement.addElement("id").setText(String.valueOf(id++));
|
||||
emailAddressElement.addElement("primary").setText("true");
|
||||
emailAddressElement.addElement("git").setText("true");
|
||||
emailAddressElement.addElement("value").setText(entry.getKey());
|
||||
emailAddressElement.addElement("owner").setText(entry.getValue());
|
||||
primaryEmailElements.put(entry.getValue(), emailAddressElement);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry: gitEmails.entrySet()) {
|
||||
if (!primaryEmails.containsKey(entry.getKey())) {
|
||||
Element emailAddressElement = listElement.addElement("io.onedev.server.model.EmailAddress");
|
||||
emailAddressElement.addAttribute("revision", "0.0");
|
||||
emailAddressElement.addElement("id").setText(String.valueOf(id++));
|
||||
emailAddressElement.addElement("primary").setText("false");
|
||||
emailAddressElement.addElement("git").setText("true");
|
||||
emailAddressElement.addElement("value").setText(entry.getKey());
|
||||
emailAddressElement.addElement("owner").setText(entry.getValue());
|
||||
primaryEmailElements.get(entry.getValue()).element("git").setText("false");
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry: alternateEmails.entrySet()) {
|
||||
if (!primaryEmails.containsKey(entry.getKey()) && !gitEmails.containsKey(entry.getKey())) {
|
||||
Element emailAddressElement = listElement.addElement("io.onedev.server.model.EmailAddress");
|
||||
emailAddressElement.addAttribute("revision", "0.0");
|
||||
emailAddressElement.addElement("id").setText(String.valueOf(id++));
|
||||
emailAddressElement.addElement("primary").setText("false");
|
||||
emailAddressElement.addElement("git").setText("false");
|
||||
emailAddressElement.addElement("value").setText(entry.getKey());
|
||||
emailAddressElement.addElement("owner").setText(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
emailAddressesDom.writeToFile(emailAddressesFile, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import io.onedev.server.web.editable.annotation.Editable;
|
||||
|
||||
@Editable
|
||||
@Entity
|
||||
@Table(indexes={@Index(columnList="o_owner_id"), @Index(columnList="value")})
|
||||
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
|
||||
public class EmailAddress extends AbstractEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String PROP_OWNER = "owner";
|
||||
|
||||
public static final String PROP_VALUE = "value";
|
||||
|
||||
@Column(nullable=false, unique=true)
|
||||
private String value;
|
||||
|
||||
@JsonIgnore
|
||||
private String verificationCode = RandomStringUtils.randomAlphanumeric(16);
|
||||
|
||||
private boolean primary;
|
||||
|
||||
private boolean git;
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private User owner;
|
||||
|
||||
@Editable
|
||||
@Email
|
||||
@NotEmpty
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Editable
|
||||
public String getVerficationCode() {
|
||||
return verificationCode;
|
||||
}
|
||||
|
||||
public void setVerificationCode(String verificationCode) {
|
||||
this.verificationCode = verificationCode;
|
||||
}
|
||||
|
||||
@Editable
|
||||
public boolean isPrimary() {
|
||||
return primary;
|
||||
}
|
||||
|
||||
public void setPrimary(boolean primary) {
|
||||
this.primary = primary;
|
||||
}
|
||||
|
||||
@Editable
|
||||
public boolean isGit() {
|
||||
return git;
|
||||
}
|
||||
|
||||
public void setGit(boolean git) {
|
||||
this.git = git;
|
||||
}
|
||||
|
||||
public User getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(User owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return getVerficationCode() == null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -61,10 +61,10 @@ import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.LastCommitsOfChildren;
|
||||
import org.eclipse.jgit.revwalk.LastCommitsOfChildren.Value;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.revwalk.LastCommitsOfChildren.Value;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
@ -91,11 +91,11 @@ import io.onedev.server.entitymanager.BuildManager;
|
||||
import io.onedev.server.entitymanager.BuildQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.CodeCommentQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.CommitQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.IssueQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.PullRequestQueryPersonalizationManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.event.RefUpdated;
|
||||
import io.onedev.server.git.BlameBlock;
|
||||
import io.onedev.server.git.Blob;
|
||||
@ -1477,11 +1477,11 @@ public class Project extends AbstractEntity {
|
||||
cmd.range(range);
|
||||
|
||||
List<User> authors = new ArrayList<>();
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
EmailAddressManager emailAddressManager = OneDev.getInstance(EmailAddressManager.class);
|
||||
for (BlameBlock block: cmd.call()) {
|
||||
User author = userManager.find(block.getCommit().getAuthor());
|
||||
if (author != null && !authors.contains(author))
|
||||
authors.add(author);
|
||||
EmailAddress emailAddress = emailAddressManager.findByPersonIdent(block.getCommit().getAuthor());
|
||||
if (emailAddress != null && emailAddress.isVerified() && !authors.contains(emailAddress.getOwner()))
|
||||
authors.add(emailAddress.getOwner());
|
||||
}
|
||||
|
||||
return authors;
|
||||
|
||||
@ -13,7 +13,6 @@ import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
import org.apache.sshd.common.config.keys.KeyUtils;
|
||||
@ -32,10 +31,7 @@ import io.onedev.server.web.editable.annotation.OmitName;
|
||||
|
||||
@Editable
|
||||
@Entity
|
||||
@Table(
|
||||
indexes={@Index(columnList="o_owner_id"), @Index(columnList="digest")},
|
||||
uniqueConstraints={@UniqueConstraint(columnNames={"digest"})}
|
||||
)
|
||||
@Table(indexes={@Index(columnList="o_owner_id"), @Index(columnList="digest")})
|
||||
@ClassValidating
|
||||
public class SshKey extends AbstractEntity implements Validatable {
|
||||
|
||||
@ -45,7 +41,7 @@ public class SshKey extends AbstractEntity implements Validatable {
|
||||
private String content;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(nullable=false)
|
||||
@Column(nullable=false, unique=true)
|
||||
private String digest;
|
||||
|
||||
@JsonIgnore
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import static io.onedev.server.model.User.PROP_ACCESS_TOKEN;
|
||||
import static io.onedev.server.model.User.PROP_EMAIL;
|
||||
import static io.onedev.server.model.User.PROP_FULL_NAME;
|
||||
import static io.onedev.server.model.User.PROP_NAME;
|
||||
import static io.onedev.server.model.User.PROP_SSO_CONNECTOR;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
@ -31,19 +31,18 @@ import org.apache.shiro.subject.Subject;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.support.NamedProjectQuery;
|
||||
import io.onedev.server.model.support.QueryPersonalization;
|
||||
import io.onedev.server.model.support.SsoInfo;
|
||||
import io.onedev.server.model.support.TwoFactorAuthentication;
|
||||
import io.onedev.server.model.support.administration.authenticator.Authenticator;
|
||||
import io.onedev.server.model.support.administration.sso.SsoConnector;
|
||||
@ -52,7 +51,6 @@ import io.onedev.server.model.support.issue.NamedIssueQuery;
|
||||
import io.onedev.server.model.support.pullrequest.NamedPullRequestQuery;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.match.MatchScoreUtils;
|
||||
import io.onedev.server.util.validation.annotation.EmailList;
|
||||
import io.onedev.server.util.validation.annotation.UserName;
|
||||
import io.onedev.server.util.watch.QuerySubscriptionSupport;
|
||||
import io.onedev.server.util.watch.QueryWatchSupport;
|
||||
@ -61,10 +59,8 @@ import io.onedev.server.web.editable.annotation.Password;
|
||||
|
||||
@Entity
|
||||
@Table(
|
||||
indexes={@Index(columnList=PROP_NAME), @Index(columnList=PROP_EMAIL),
|
||||
@Index(columnList=PROP_FULL_NAME), @Index(columnList=SsoInfo.COLUMN_CONNECTOR),
|
||||
@Index(columnList=SsoInfo.COLUMN_SUBJECT), @Index(columnList=PROP_ACCESS_TOKEN)},
|
||||
uniqueConstraints={@UniqueConstraint(columnNames={SsoInfo.COLUMN_CONNECTOR, SsoInfo.COLUMN_SUBJECT})})
|
||||
indexes={@Index(columnList=PROP_NAME), @Index(columnList=PROP_FULL_NAME),
|
||||
@Index(columnList=PROP_SSO_CONNECTOR), @Index(columnList=PROP_ACCESS_TOKEN)})
|
||||
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
|
||||
@Editable
|
||||
public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
@ -87,26 +83,14 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
|
||||
public static final String PROP_NAME = "name";
|
||||
|
||||
public static final String PROP_EMAIL = "email";
|
||||
|
||||
public static final String PROP_GIT_EMAIL = "gitEmail";
|
||||
|
||||
public static final String PROP_ALTERNATE_EMAILS = "alternateEmails";
|
||||
|
||||
public static final String PROP_PASSWORD = "password";
|
||||
|
||||
public static final String PROP_FULL_NAME = "fullName";
|
||||
|
||||
public static final String PROP_SSO_INFO = "ssoInfo";
|
||||
public static final String PROP_SSO_CONNECTOR = "ssoConnector";
|
||||
|
||||
public static final String PROP_ACCESS_TOKEN = "accessToken";
|
||||
|
||||
public static final String AUTH_SOURCE_BUILTIN_USER_STORE = "Builtin User Store";
|
||||
|
||||
public static final String AUTH_SOURCE_EXTERNAL_AUTHENTICATOR = "External Authenticator";
|
||||
|
||||
public static final String AUTH_SOURCE_SSO_PROVIDER = "SSO Provider: ";
|
||||
|
||||
private static ThreadLocal<Stack<User>> stack = new ThreadLocal<Stack<User>>() {
|
||||
|
||||
@Override
|
||||
@ -126,20 +110,10 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
private String fullName;
|
||||
|
||||
@JsonIgnore
|
||||
@Embedded
|
||||
private SsoInfo ssoInfo = new SsoInfo();
|
||||
|
||||
@Column(unique=true, nullable=false)
|
||||
private String email;
|
||||
|
||||
@Column
|
||||
private String gitEmail;
|
||||
|
||||
@Lob
|
||||
@Column(nullable=false, length=1024)
|
||||
private ArrayList<String> alternateEmails = new ArrayList<>();
|
||||
private String ssoConnector;
|
||||
|
||||
@Column(unique=true, nullable=false)
|
||||
@JsonIgnore
|
||||
private String accessToken = RandomStringUtils.randomAlphanumeric(ACCESS_TOKEN_LEN);
|
||||
|
||||
@JsonIgnore
|
||||
@ -189,6 +163,10 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
|
||||
private Collection<SshKey> sshKeys = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy="owner", cascade=CascadeType.REMOVE)
|
||||
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
|
||||
private Collection<EmailAddress> emailAddresses = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
@Lob
|
||||
@Column(nullable=false, length=65535)
|
||||
@ -226,6 +204,8 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
|
||||
private transient Collection<Group> groups;
|
||||
|
||||
private transient List<EmailAddress> sortedEmailAddresses;
|
||||
|
||||
public QueryPersonalization<NamedProjectQuery> getProjectQueryPersonalization() {
|
||||
return new QueryPersonalization<NamedProjectQuery>() {
|
||||
|
||||
@ -465,13 +445,14 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public SsoInfo getSsoInfo() {
|
||||
return ssoInfo;
|
||||
|
||||
@Nullable
|
||||
public String getSsoConnector() {
|
||||
return ssoConnector;
|
||||
}
|
||||
|
||||
public void setSsoInfo(SsoInfo ssoInfo) {
|
||||
this.ssoInfo = ssoInfo;
|
||||
public void setSsoConnector(String ssoConnector) {
|
||||
this.ssoConnector = ssoConnector;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
@ -491,39 +472,6 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
this.twoFactorAuthentication = twoFactorAuthentication;
|
||||
}
|
||||
|
||||
@Editable(order=300)
|
||||
@NotEmpty
|
||||
@Email
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Editable(order=350, description="Specify an email to use for web based git operations if you want to "
|
||||
+ "keep your primary email secret")
|
||||
public String getGitEmail() {
|
||||
return gitEmail;
|
||||
}
|
||||
|
||||
public void setGitEmail(String gitEmail) {
|
||||
this.gitEmail = gitEmail;
|
||||
}
|
||||
|
||||
@Editable(order=400, description="Optionally specify one or more alternate emails with one email per line. "
|
||||
+ "With alternate emails, git commits authored/committed via your old emails can be associated with "
|
||||
+ "your current account")
|
||||
@EmailList
|
||||
public ArrayList<String> getAlternateEmails() {
|
||||
return alternateEmails;
|
||||
}
|
||||
|
||||
public void setAlternateEmails(ArrayList<String> alternateEmails) {
|
||||
this.alternateEmails = alternateEmails;
|
||||
}
|
||||
|
||||
public Collection<Membership> getMemberships() {
|
||||
return memberships;
|
||||
}
|
||||
@ -532,14 +480,6 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
this.memberships = memberships;
|
||||
}
|
||||
|
||||
public Collection<String> getEmails() {
|
||||
Collection<String> emails = Sets.newHashSet(email);
|
||||
if (gitEmail != null)
|
||||
emails.add(gitEmail);
|
||||
emails.addAll(alternateEmails);
|
||||
return emails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
@ -547,13 +487,17 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PersonIdent asPerson() {
|
||||
if (isSystem())
|
||||
if (isSystem()) {
|
||||
return new PersonIdent(getDisplayName(), "");
|
||||
else if (getGitEmail() != null)
|
||||
return new PersonIdent(getDisplayName(), getGitEmail());
|
||||
else
|
||||
return new PersonIdent(getDisplayName(), getEmail());
|
||||
} else {
|
||||
EmailAddress emailAddress = getGitEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
return new PersonIdent(getDisplayName(), emailAddress.getValue());
|
||||
else
|
||||
throw new ExplicitException("No verified email for git operations");
|
||||
}
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
@ -629,9 +573,17 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
this.sshKeys = sshKeys;
|
||||
}
|
||||
|
||||
public boolean isSshKeyExternalManaged() {
|
||||
public Collection<EmailAddress> getEmailAddresses() {
|
||||
return emailAddresses;
|
||||
}
|
||||
|
||||
public void setEmailAddresses(Collection<EmailAddress> emailAddresses) {
|
||||
this.emailAddresses = emailAddresses;
|
||||
}
|
||||
|
||||
public boolean isSshKeyExternalManaged() {
|
||||
if (isExternalManaged()) {
|
||||
if (getSsoInfo().getConnector() != null) {
|
||||
if (getSsoConnector() != null) {
|
||||
return false;
|
||||
} else {
|
||||
Authenticator authenticator = OneDev.getInstance(SettingManager.class).getAuthenticator();
|
||||
@ -645,9 +597,9 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
public boolean isMembershipExternalManaged() {
|
||||
if (isExternalManaged()) {
|
||||
SettingManager settingManager = OneDev.getInstance(SettingManager.class);
|
||||
if (getSsoInfo().getConnector() != null) {
|
||||
if (getSsoConnector() != null) {
|
||||
SsoConnector ssoConnector = settingManager.getSsoConnectors().stream()
|
||||
.filter(it->it.getName().equals(getSsoInfo().getConnector()))
|
||||
.filter(it->it.getName().equals(getSsoConnector()))
|
||||
.findFirst().orElse(null);
|
||||
return ssoConnector != null && ssoConnector.isManagingMemberships();
|
||||
} else {
|
||||
@ -661,12 +613,12 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
|
||||
public String getAuthSource() {
|
||||
if (isExternalManaged()) {
|
||||
if (getSsoInfo().getConnector() != null)
|
||||
return AUTH_SOURCE_SSO_PROVIDER + getSsoInfo().getConnector();
|
||||
if (getSsoConnector() != null)
|
||||
return "SSO Provider: " + getSsoConnector();
|
||||
else
|
||||
return AUTH_SOURCE_EXTERNAL_AUTHENTICATOR;
|
||||
return "External Authenticator";
|
||||
} else {
|
||||
return AUTH_SOURCE_BUILTIN_USER_STORE;
|
||||
return "Builtin User Store";
|
||||
}
|
||||
}
|
||||
|
||||
@ -811,4 +763,40 @@ public class User extends AbstractEntity implements AuthenticationInfo {
|
||||
|| getGroups().stream().anyMatch(it->it.isEnforce2FA());
|
||||
}
|
||||
|
||||
public List<EmailAddress> getSortedEmailAddresses() {
|
||||
if (sortedEmailAddresses == null) {
|
||||
sortedEmailAddresses = new ArrayList<>(getEmailAddresses());
|
||||
Collections.sort(sortedEmailAddresses, new Comparator<EmailAddress>() {
|
||||
|
||||
@Override
|
||||
public int compare(EmailAddress o1, EmailAddress o2) {
|
||||
if (o1.isPrimary() && o2.isPrimary() || !o1.isPrimary() && !o2.isPrimary()) {
|
||||
if (o1.isGit() && o2.isGit() || !o1.isGit() && !o2.isGit())
|
||||
return o1.getId().compareTo(o2.getId());
|
||||
else if (o1.isGit())
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
} else if (o1.isPrimary()) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
return sortedEmailAddresses;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public EmailAddress getPrimaryEmailAddress() {
|
||||
return getSortedEmailAddresses().stream().filter(it->it.isPrimary()).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public EmailAddress getGitEmailAddress() {
|
||||
return getSortedEmailAddresses().stream().filter(it->it.isGit()).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
package io.onedev.server.model.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
@Embeddable
|
||||
public class SsoInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String COLUMN_CONNECTOR = "SSO_CONNECTOR";
|
||||
|
||||
public static final String COLUMN_SUBJECT = "SSO_SUBJECT";
|
||||
|
||||
public static final String PROP_CONNECTOR = "connector";
|
||||
|
||||
public static final String PROP_SUBJECT = "subject";
|
||||
|
||||
@Column(name=COLUMN_CONNECTOR)
|
||||
private String connector;
|
||||
|
||||
/*
|
||||
* SQL Server treats null as a value when checking unique constraints. So we
|
||||
* need to populate subject even if no SSO is used to avoid violating unique
|
||||
* constraint on connector and subject combo
|
||||
*/
|
||||
@Column(name=COLUMN_SUBJECT, nullable=false)
|
||||
private String subject = UUID.randomUUID().toString();
|
||||
|
||||
@Nullable
|
||||
public String getConnector() {
|
||||
return connector;
|
||||
}
|
||||
|
||||
public void setConnector(String connector) {
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public void setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,32 +7,24 @@ import javax.annotation.Nullable;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.SsoInfo;
|
||||
import io.onedev.server.model.support.administration.authenticator.Authenticated;
|
||||
|
||||
public class SsoAuthenticated extends Authenticated implements AuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String subject;
|
||||
|
||||
private final String userName;
|
||||
|
||||
private final SsoConnector connector;
|
||||
|
||||
public SsoAuthenticated(String subject, String userName, String email, @Nullable String fullName,
|
||||
public SsoAuthenticated(String userName, String email, @Nullable String fullName,
|
||||
@Nullable Collection<String> groupNames, @Nullable Collection<String> sshKeys,
|
||||
SsoConnector connector) {
|
||||
super(email, fullName, groupNames, sshKeys);
|
||||
this.subject = subject;
|
||||
this.userName = userName;
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
@ -51,10 +43,4 @@ public class SsoAuthenticated extends Authenticated implements AuthenticationTok
|
||||
return User.EXTERNAL_MANAGED;
|
||||
}
|
||||
|
||||
public SsoInfo getSsoInfo() {
|
||||
SsoInfo ssoInfo = new SsoInfo();
|
||||
ssoInfo.setConnector(connector.getName());
|
||||
ssoInfo.setSubject(subject);
|
||||
return ssoInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,9 +50,8 @@ public enum MergeStrategy {
|
||||
ObjectId requestHead = request.getLatestUpdate().getHeadCommit();
|
||||
ObjectId targetHead = request.getTarget().getObjectId();
|
||||
PersonIdent committer = new PersonIdent(User.SYSTEM_NAME, "");
|
||||
PersonIdent author = request.getSubmitter().asPerson();
|
||||
return GitUtils.merge(repository, targetHead, requestHead, true, committer, author,
|
||||
commitMessage, false);
|
||||
return GitUtils.merge(repository, targetHead, requestHead, true, committer,
|
||||
request.getSubmitter().asPerson(), commitMessage, false);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package io.onedev.server.notification;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -18,8 +19,10 @@ import io.onedev.server.event.issue.IssueEvent;
|
||||
import io.onedev.server.event.pullrequest.PullRequestEvent;
|
||||
import io.onedev.server.markdown.MarkdownManager;
|
||||
import io.onedev.server.model.AbstractEntity;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.PullRequestAssignment;
|
||||
import io.onedev.server.model.PullRequestReview;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.administration.notificationtemplate.NotificationTemplateSetting;
|
||||
|
||||
public abstract class AbstractNotificationManager {
|
||||
@ -100,4 +103,12 @@ public abstract class AbstractNotificationManager {
|
||||
return textBody.toString();
|
||||
}
|
||||
|
||||
protected boolean isNotified(Collection<String> notifiedEmailAddresses, User user) {
|
||||
for (EmailAddress emailAddress: user.getEmailAddresses()) {
|
||||
if (emailAddress.isVerified() && notifiedEmailAddresses.contains(emailAddress.getValue()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import io.onedev.server.event.build.BuildUpdated;
|
||||
import io.onedev.server.markdown.MarkdownManager;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.BuildQueryPersonalization;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.NamedQuery;
|
||||
@ -107,7 +108,9 @@ public class BuildNotificationManager extends AbstractNotificationManager {
|
||||
User.push(user);
|
||||
try {
|
||||
if (BuildQuery.parse(event.getProject(), queryString, true, true).matches(build)) {
|
||||
notifyEmails.add(user.getEmail());
|
||||
EmailAddress emailAddress = user.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
notifyEmails.add(emailAddress.getValue());
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -142,7 +145,9 @@ public class BuildNotificationManager extends AbstractNotificationManager {
|
||||
User.push(user);
|
||||
try {
|
||||
if (BuildQuery.parse(null, queryString, true, true).matches(build)) {
|
||||
notifyEmails.add(user.getEmail());
|
||||
EmailAddress emailAddress = user.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
notifyEmails.add(emailAddress.getValue());
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -15,6 +15,7 @@ import io.onedev.server.event.codecomment.CodeCommentEvent;
|
||||
import io.onedev.server.event.codecomment.CodeCommentReplied;
|
||||
import io.onedev.server.markdown.MarkdownManager;
|
||||
import io.onedev.server.markdown.MentionParser;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
|
||||
@ -63,11 +64,14 @@ public class CodeCommentNotificationManager extends AbstractNotificationManager
|
||||
String threadingReferences = "<" + event.getComment().getProject().getPath()
|
||||
+ "-codecomment-" + event.getComment().getId() + "@onedev>";
|
||||
|
||||
mailManager.sendMailAsync(Sets.newHashSet(user.getEmail()), Lists.newArrayList(),
|
||||
Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, processedMarkdown, url, false, null),
|
||||
getTextBody(event, summary, markdown, url, false, null),
|
||||
null, threadingReferences);
|
||||
EmailAddress emailAddress = user.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Sets.newHashSet(emailAddress.getValue()), Lists.newArrayList(),
|
||||
Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, processedMarkdown, url, false, null),
|
||||
getTextBody(event, summary, markdown, url, false, null),
|
||||
null, threadingReferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ import io.onedev.server.event.RefUpdated;
|
||||
import io.onedev.server.git.GitUtils;
|
||||
import io.onedev.server.markdown.MarkdownManager;
|
||||
import io.onedev.server.model.CommitQueryPersonalization;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.NamedQuery;
|
||||
@ -87,7 +88,9 @@ public class CommitNotificationManager extends AbstractNotificationManager {
|
||||
User.push(user);
|
||||
try {
|
||||
if (CommitQuery.parse(project, queryString).matches(event)) {
|
||||
notifyEmails.add(user.getEmail());
|
||||
EmailAddress emailAddress = user.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
notifyEmails.add(emailAddress.getValue());
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -63,6 +63,7 @@ import io.onedev.commons.loader.Listen;
|
||||
import io.onedev.commons.utils.ExceptionUtils;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.entitymanager.IssueManager;
|
||||
import io.onedev.server.entitymanager.IssueWatchManager;
|
||||
@ -77,6 +78,7 @@ import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.event.system.SystemStopping;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.IssueWatch;
|
||||
@ -100,7 +102,7 @@ import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.security.permission.AccessProject;
|
||||
import io.onedev.server.security.permission.ProjectPermission;
|
||||
import io.onedev.server.security.permission.ReadCode;
|
||||
import io.onedev.server.util.EmailAddress;
|
||||
import io.onedev.server.util.ParsedEmailAddress;
|
||||
import io.onedev.server.util.HtmlUtils;
|
||||
import io.onedev.server.util.validation.UserNameValidator;
|
||||
|
||||
@ -133,6 +135,8 @@ public class DefaultMailManager implements MailManager {
|
||||
|
||||
private final UserManager userManager;
|
||||
|
||||
private final EmailAddressManager emailAddressManager;
|
||||
|
||||
private final UrlManager urlManager;
|
||||
|
||||
private volatile Thread thread;
|
||||
@ -144,7 +148,7 @@ public class DefaultMailManager implements MailManager {
|
||||
IssueCommentManager issueCommentManager, IssueWatchManager issueWatchManager,
|
||||
PullRequestManager pullRequestManager, PullRequestCommentManager pullRequestCommentManager,
|
||||
PullRequestWatchManager pullRequestWatchManager, ExecutorService executorService,
|
||||
UrlManager urlManager) {
|
||||
UrlManager urlManager, EmailAddressManager emailAddressManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
this.settingManager = setingManager;
|
||||
this.userManager = userManager;
|
||||
@ -158,6 +162,7 @@ public class DefaultMailManager implements MailManager {
|
||||
this.pullRequestWatchManager = pullRequestWatchManager;
|
||||
this.executorService = executorService;
|
||||
this.urlManager = urlManager;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@ -311,158 +316,172 @@ public class DefaultMailManager implements MailManager {
|
||||
|
||||
InternetAddress from = InternetAddress.parse(fromHeader[0], true)[0];
|
||||
|
||||
User user = userManager.findByEmail(from.getAddress());
|
||||
EmailAddress fromAddressEntity = emailAddressManager.findByValue(from.getAddress());
|
||||
if (fromAddressEntity != null && !fromAddressEntity.isVerified()) {
|
||||
logger.error("Another account uses email address '{}' but not verified", from.getAddress());
|
||||
} else {
|
||||
User user = fromAddressEntity != null?fromAddressEntity.getOwner():null;
|
||||
SenderAuthorization authorization = null;
|
||||
String designatedProject = null;
|
||||
ServiceDeskSetting serviceDeskSetting = settingManager.getServiceDeskSetting();
|
||||
if (serviceDeskSetting != null) {
|
||||
authorization = serviceDeskSetting.getSenderAuthorization(from.getAddress());
|
||||
designatedProject = serviceDeskSetting.getDesignatedProject(from.getAddress());
|
||||
}
|
||||
ParsedEmailAddress parsedSystemAddress = ParsedEmailAddress.parse(mailSetting.getEmailAddress());
|
||||
|
||||
Collection<Issue> issues = new ArrayList<>();
|
||||
Collection<PullRequest> pullRequests = new ArrayList<>();
|
||||
Collection<InternetAddress> involved = new ArrayList<>();
|
||||
|
||||
SenderAuthorization authorization = null;
|
||||
String designatedProject = null;
|
||||
ServiceDeskSetting serviceDeskSetting = settingManager.getServiceDeskSetting();
|
||||
if (serviceDeskSetting != null) {
|
||||
authorization = serviceDeskSetting.getSenderAuthorization(from.getAddress());
|
||||
designatedProject = serviceDeskSetting.getDesignatedProject(from.getAddress());
|
||||
}
|
||||
EmailAddress systemAddress = EmailAddress.parse(mailSetting.getEmailAddress());
|
||||
|
||||
Collection<Issue> issues = new ArrayList<>();
|
||||
Collection<PullRequest> pullRequests = new ArrayList<>();
|
||||
Collection<InternetAddress> involved = new ArrayList<>();
|
||||
|
||||
List<InternetAddress> receivers = new ArrayList<>();
|
||||
receivers.addAll(Arrays.asList(InternetAddress.parse(toHeader[0], true)));
|
||||
|
||||
if (ccHeader != null && ccHeader.length != 0)
|
||||
receivers.addAll(Arrays.asList(InternetAddress.parse(ccHeader[0], true)));
|
||||
|
||||
List<String> receiverEmailAddresses =
|
||||
receivers.stream().map(it->it.getAddress()).collect(Collectors.toList());
|
||||
|
||||
for (InternetAddress receiver: receivers) {
|
||||
EmailAddress receiverAddress = EmailAddress.parse(receiver.getAddress());
|
||||
if (receiverAddress.toString().equals(systemAddress.toString())) {
|
||||
if (serviceDeskSetting != null) {
|
||||
if (designatedProject == null)
|
||||
throw new ExplicitException("No project designated for sender: " + from.getAddress());
|
||||
Project project = projectManager.findByPath(designatedProject);
|
||||
if (project == null) {
|
||||
String errorMessage = String.format(
|
||||
"Sender project does not exist (sender: %s, project: %s)",
|
||||
from.getAddress(), designatedProject);
|
||||
throw new ExplicitException(errorMessage);
|
||||
}
|
||||
checkPermission(from, project, new AccessProject(), user, authorization);
|
||||
issues.add(openIssue(message, project, from, user, authorization));
|
||||
} else {
|
||||
throw new ExplicitException("Unable to create issue from email as service desk is not enabled");
|
||||
}
|
||||
} else if (receiverAddress.getDomain().equals(systemAddress.getDomain())
|
||||
&& receiverAddress.getName().startsWith(systemAddress.getName() + "+")) {
|
||||
String subAddress = receiverAddress.getName().substring(systemAddress.getName().length()+1);
|
||||
if (subAddress.equals(MailManager.TEST_SUB_ADDRESS)) {
|
||||
continue;
|
||||
} else if (subAddress.contains("~")) {
|
||||
Long entityId;
|
||||
try {
|
||||
entityId = Long.parseLong(StringUtils.substringAfter(subAddress, "~"));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ExplicitException("Invalid id specified in receipient address: " + receiverAddress);
|
||||
}
|
||||
if (subAddress.contains("issue")) {
|
||||
Issue issue = issueManager.get(entityId);
|
||||
if (issue == null)
|
||||
throw new ExplicitException("Non-existent issue specified in receipient address: " + receiverAddress);
|
||||
if (subAddress.contains("unsubscribe")) {
|
||||
if (user != null) {
|
||||
IssueWatch watch = issueWatchManager.find(issue, user);
|
||||
if (watch != null) {
|
||||
watch.setWatching(false);
|
||||
issueWatchManager.save(watch);
|
||||
String subject = "Unsubscribed successfully from issue " + issue.getFQN();
|
||||
String body = "You will no longer receive notifications of issue " + issue.getFQN() + " unless mentioned. "
|
||||
+ "However if you subscribed to certain issue queries, you may still get notifications of newly "
|
||||
+ "created issues matching those queries. In this case, you will need to login to your account "
|
||||
+ "and unsubscribe those queries.";
|
||||
sendMailAsync(Lists.newArrayList(from.getAddress()), Lists.newArrayList(), Lists.newArrayList(),
|
||||
subject, body, body, null, getMessageId(message));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkPermission(from, issue.getProject(), new AccessProject(), user, authorization);
|
||||
addComment(issue, message, from, receiverEmailAddresses, user, authorization);
|
||||
issues.add(issue);
|
||||
}
|
||||
} else if (subAddress.contains("pullrequest")) {
|
||||
PullRequest pullRequest = pullRequestManager.get(entityId);
|
||||
if (pullRequest == null)
|
||||
throw new ExplicitException("Non-existent pull request specified in receipient address: " + receiverAddress);
|
||||
if (subAddress.contains("unsubscribe")) {
|
||||
if (user != null) {
|
||||
PullRequestWatch watch = pullRequestWatchManager.find(pullRequest, user);
|
||||
if (watch != null) {
|
||||
watch.setWatching(false);
|
||||
pullRequestWatchManager.save(watch);
|
||||
String subject = "Unsubscribed successfully from pull request " + pullRequest.getFQN();
|
||||
String body = "You will no longer receive notifications of pull request " + pullRequest.getFQN()
|
||||
+ " unless mentioned. However if you subscribed to certain pull request queries, you may still "
|
||||
+ "get notifications of newly submitted pull request matching those queries. In this case, you "
|
||||
+ "will need to login to your account and unsubscribe those queries.";
|
||||
sendMailAsync(Lists.newArrayList(from.getAddress()), Lists.newArrayList(), Lists.newArrayList(),
|
||||
subject, body, body, null, getMessageId(message));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkPermission(from, pullRequest.getTargetProject(), new ReadCode(), user, authorization);
|
||||
addComment(pullRequest, message, from, receiverEmailAddresses, user, authorization);
|
||||
pullRequests.add(pullRequest);
|
||||
}
|
||||
} else {
|
||||
throw new ExplicitException("Invalid receipient address: " + receiverAddress);
|
||||
}
|
||||
} else {
|
||||
Project project = projectManager.findByServiceDeskName(subAddress);
|
||||
if (project == null)
|
||||
project = projectManager.findByPath(subAddress);
|
||||
|
||||
if (project == null)
|
||||
throw new ExplicitException("Non-existent project specified in receipient address: " + receiverAddress);
|
||||
List<InternetAddress> receivers = new ArrayList<>();
|
||||
receivers.addAll(Arrays.asList(InternetAddress.parse(toHeader[0], true)));
|
||||
|
||||
if (ccHeader != null && ccHeader.length != 0)
|
||||
receivers.addAll(Arrays.asList(InternetAddress.parse(ccHeader[0], true)));
|
||||
|
||||
List<String> receiverEmailAddresses =
|
||||
receivers.stream().map(it->it.getAddress()).collect(Collectors.toList());
|
||||
|
||||
for (InternetAddress receiver: receivers) {
|
||||
ParsedEmailAddress parsedReceiverAddress = ParsedEmailAddress.parse(receiver.getAddress());
|
||||
if (parsedReceiverAddress.toString().equals(parsedSystemAddress.toString())) {
|
||||
if (serviceDeskSetting != null) {
|
||||
if (designatedProject == null)
|
||||
throw new ExplicitException("No project designated for sender: " + from.getAddress());
|
||||
Project project = projectManager.findByPath(designatedProject);
|
||||
if (project == null) {
|
||||
String errorMessage = String.format(
|
||||
"Sender project does not exist (sender: %s, project: %s)",
|
||||
from.getAddress(), designatedProject);
|
||||
throw new ExplicitException(errorMessage);
|
||||
}
|
||||
checkPermission(from, project, new AccessProject(), user, authorization);
|
||||
logger.debug("Creating issue via email (project: {})...", project.getPath());
|
||||
issues.add(openIssue(message, project, from, user, authorization));
|
||||
} else {
|
||||
throw new ExplicitException("Unable to create issue from email as service desk is not enabled");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
involved.add(receiver);
|
||||
}
|
||||
}
|
||||
|
||||
for (Issue issue: issues) {
|
||||
for (InternetAddress each: involved) {
|
||||
user = userManager.findByEmail(each.getAddress());
|
||||
if (serviceDeskSetting != null)
|
||||
authorization = serviceDeskSetting.getSenderAuthorization(each.getAddress());
|
||||
try {
|
||||
checkPermission(each, issue.getProject(), new AccessProject(), user, authorization);
|
||||
if (user == null)
|
||||
user = createUserIfNotExist(each, issue.getProject(), authorization.getAuthorizedRole());
|
||||
watch(user, issue);
|
||||
} catch (UnauthorizedException e) {
|
||||
logger.error("Error adding receipient to watch list", e);
|
||||
} else if (parsedReceiverAddress.getDomain().equals(parsedSystemAddress.getDomain())
|
||||
&& parsedReceiverAddress.getName().startsWith(parsedSystemAddress.getName() + "+")) {
|
||||
String subAddress = parsedReceiverAddress.getName().substring(parsedSystemAddress.getName().length()+1);
|
||||
if (subAddress.equals(MailManager.TEST_SUB_ADDRESS)) {
|
||||
continue;
|
||||
} else if (subAddress.contains("~")) {
|
||||
Long entityId;
|
||||
try {
|
||||
entityId = Long.parseLong(StringUtils.substringAfter(subAddress, "~"));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ExplicitException("Invalid id specified in receipient address: " + parsedReceiverAddress);
|
||||
}
|
||||
if (subAddress.contains("issue")) {
|
||||
Issue issue = issueManager.get(entityId);
|
||||
if (issue == null)
|
||||
throw new ExplicitException("Non-existent issue specified in receipient address: " + parsedReceiverAddress);
|
||||
if (subAddress.contains("unsubscribe")) {
|
||||
if (user != null) {
|
||||
IssueWatch watch = issueWatchManager.find(issue, user);
|
||||
if (watch != null) {
|
||||
watch.setWatching(false);
|
||||
issueWatchManager.save(watch);
|
||||
String subject = "Unsubscribed successfully from issue " + issue.getFQN();
|
||||
String body = "You will no longer receive notifications of issue " + issue.getFQN() + " unless mentioned. "
|
||||
+ "However if you subscribed to certain issue queries, you may still get notifications of newly "
|
||||
+ "created issues matching those queries. In this case, you will need to login to your account "
|
||||
+ "and unsubscribe those queries.";
|
||||
sendMailAsync(Lists.newArrayList(from.getAddress()), Lists.newArrayList(), Lists.newArrayList(),
|
||||
subject, body, body, null, getMessageId(message));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkPermission(from, issue.getProject(), new AccessProject(), user, authorization);
|
||||
addComment(issue, message, from, receiverEmailAddresses, user, authorization);
|
||||
issues.add(issue);
|
||||
}
|
||||
} else if (subAddress.contains("pullrequest")) {
|
||||
PullRequest pullRequest = pullRequestManager.get(entityId);
|
||||
if (pullRequest == null)
|
||||
throw new ExplicitException("Non-existent pull request specified in receipient address: " + parsedReceiverAddress);
|
||||
if (subAddress.contains("unsubscribe")) {
|
||||
if (user != null) {
|
||||
PullRequestWatch watch = pullRequestWatchManager.find(pullRequest, user);
|
||||
if (watch != null) {
|
||||
watch.setWatching(false);
|
||||
pullRequestWatchManager.save(watch);
|
||||
String subject = "Unsubscribed successfully from pull request " + pullRequest.getFQN();
|
||||
String body = "You will no longer receive notifications of pull request " + pullRequest.getFQN()
|
||||
+ " unless mentioned. However if you subscribed to certain pull request queries, you may still "
|
||||
+ "get notifications of newly submitted pull request matching those queries. In this case, you "
|
||||
+ "will need to login to your account and unsubscribe those queries.";
|
||||
sendMailAsync(Lists.newArrayList(from.getAddress()), Lists.newArrayList(), Lists.newArrayList(),
|
||||
subject, body, body, null, getMessageId(message));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkPermission(from, pullRequest.getTargetProject(), new ReadCode(), user, authorization);
|
||||
addComment(pullRequest, message, from, receiverEmailAddresses, user, authorization);
|
||||
pullRequests.add(pullRequest);
|
||||
}
|
||||
} else {
|
||||
throw new ExplicitException("Invalid receipient address: " + parsedReceiverAddress);
|
||||
}
|
||||
} else {
|
||||
Project project = projectManager.findByServiceDeskName(subAddress);
|
||||
if (project == null)
|
||||
project = projectManager.findByPath(subAddress);
|
||||
|
||||
if (project == null)
|
||||
throw new ExplicitException("Non-existent project specified in receipient address: " + parsedReceiverAddress);
|
||||
if (serviceDeskSetting != null) {
|
||||
checkPermission(from, project, new AccessProject(), user, authorization);
|
||||
logger.debug("Creating issue via email (project: {})...", project.getPath());
|
||||
issues.add(openIssue(message, project, from, user, authorization));
|
||||
} else {
|
||||
throw new ExplicitException("Unable to create issue from email as service desk is not enabled");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
involved.add(receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PullRequest pullRequest: pullRequests) {
|
||||
for (InternetAddress each: involved) {
|
||||
user = userManager.findByEmail(each.getAddress());
|
||||
if (serviceDeskSetting != null)
|
||||
authorization = serviceDeskSetting.getSenderAuthorization(each.getAddress());
|
||||
try {
|
||||
checkPermission(each, pullRequest.getProject(), new ReadCode(), user, authorization);
|
||||
if (user == null)
|
||||
user = createUserIfNotExist(each, pullRequest.getProject(), authorization.getAuthorizedRole());
|
||||
watch(user, pullRequest);
|
||||
} catch (UnauthorizedException e) {
|
||||
logger.error("Error adding receipient to watch list", e);
|
||||
|
||||
for (Issue issue: issues) {
|
||||
for (InternetAddress each: involved) {
|
||||
EmailAddress emailAddressEntity = emailAddressManager.findByValue(each.getAddress());
|
||||
if (emailAddressEntity != null && !emailAddressEntity.isVerified()) {
|
||||
logger.error("Another account uses email address '{}' but not verified", each.getAddress());
|
||||
} else {
|
||||
if (serviceDeskSetting != null)
|
||||
authorization = serviceDeskSetting.getSenderAuthorization(each.getAddress());
|
||||
user = emailAddressEntity != null?emailAddressEntity.getOwner():null;
|
||||
try {
|
||||
checkPermission(each, issue.getProject(), new AccessProject(), user, authorization);
|
||||
if (user == null)
|
||||
user = createUser(each, issue.getProject(), authorization.getAuthorizedRole());
|
||||
watch(user, issue);
|
||||
} catch (UnauthorizedException e) {
|
||||
logger.error("Error adding receipient to watch list", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PullRequest pullRequest: pullRequests) {
|
||||
for (InternetAddress each: involved) {
|
||||
EmailAddress emailAddressEntity = emailAddressManager.findByValue(each.getAddress());
|
||||
if (emailAddressEntity != null && !emailAddressEntity.isVerified()) {
|
||||
logger.error("Another account uses email address '{}' but not verified", each.getAddress());
|
||||
} else {
|
||||
user = emailAddressEntity != null?emailAddressEntity.getOwner():null;
|
||||
if (serviceDeskSetting != null)
|
||||
authorization = serviceDeskSetting.getSenderAuthorization(each.getAddress());
|
||||
try {
|
||||
checkPermission(each, pullRequest.getProject(), new ReadCode(), user, authorization);
|
||||
if (user == null)
|
||||
user = createUser(each, pullRequest.getProject(), authorization.getAuthorizedRole());
|
||||
watch(user, pullRequest);
|
||||
} catch (UnauthorizedException e) {
|
||||
logger.error("Error adding receipient to watch list", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +564,7 @@ public class DefaultMailManager implements MailManager {
|
||||
IssueComment comment = new IssueComment();
|
||||
comment.setIssue(issue);
|
||||
if (user == null)
|
||||
user = createUserIfNotExist(author, issue.getProject(), authorization.getAuthorizedRole());
|
||||
user = createUser(author, issue.getProject(), authorization.getAuthorizedRole());
|
||||
comment.setUser(user);
|
||||
String content = stripQuotation(readText(issue.getProject(), issue.getUUID(), message));
|
||||
if (content != null) {
|
||||
@ -560,7 +579,7 @@ public class DefaultMailManager implements MailManager {
|
||||
PullRequestComment comment = new PullRequestComment();
|
||||
comment.setRequest(pullRequest);
|
||||
if (user == null)
|
||||
user = createUserIfNotExist(author, pullRequest.getProject(), authorization.getAuthorizedRole());
|
||||
user = createUser(author, pullRequest.getProject(), authorization.getAuthorizedRole());
|
||||
comment.setUser(user);
|
||||
String content = stripQuotation(readText(pullRequest.getProject(), pullRequest.getUUID(), message));
|
||||
if (content != null) {
|
||||
@ -596,7 +615,7 @@ public class DefaultMailManager implements MailManager {
|
||||
issue.setDescription(description);
|
||||
|
||||
if (user == null)
|
||||
user = createUserIfNotExist(submitter, project, authorization.getAuthorizedRole());
|
||||
user = createUser(submitter, project, authorization.getAuthorizedRole());
|
||||
issue.setSubmitter(user);
|
||||
|
||||
GlobalIssueSetting issueSetting = settingManager.getIssueSetting();
|
||||
@ -622,17 +641,21 @@ public class DefaultMailManager implements MailManager {
|
||||
issue.getEffectiveThreadingReference());
|
||||
return issue;
|
||||
}
|
||||
|
||||
private User createUserIfNotExist(InternetAddress address, Project project, Role role) {
|
||||
User user = userManager.findByEmail(address.getAddress());
|
||||
if (user == null) {
|
||||
user = new User();
|
||||
user.setName(UserNameValidator.suggestUserName(EmailAddress.parse(address.getAddress()).getName()));
|
||||
user.setEmail(address.getAddress());
|
||||
user.setFullName(address.getPersonal());
|
||||
user.setPassword("impossible password");
|
||||
userManager.save(user);
|
||||
}
|
||||
|
||||
private User createUser(InternetAddress address, Project project, Role role) {
|
||||
User user = new User();
|
||||
user.setName(UserNameValidator.suggestUserName(ParsedEmailAddress.parse(address.getAddress()).getName()));
|
||||
user.setFullName(address.getPersonal());
|
||||
user.setPassword("impossible password");
|
||||
userManager.save(user);
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setValue(address.getAddress());
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddress.setPrimary(true);
|
||||
emailAddress.setGit(true);
|
||||
emailAddress.setOwner(user);
|
||||
emailAddressManager.save(emailAddress);
|
||||
|
||||
boolean found = false;
|
||||
for (UserAuthorization authorization: user.getAuthorizations()) {
|
||||
@ -648,6 +671,7 @@ public class DefaultMailManager implements MailManager {
|
||||
authorization.setRole(role);
|
||||
authorizationManager.save(authorization);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
@ -854,7 +878,7 @@ public class DefaultMailManager implements MailManager {
|
||||
public String getReplyAddress(Issue issue) {
|
||||
MailSetting mailSetting = settingManager.getMailSetting();
|
||||
if (mailSetting != null && mailSetting.getReceiveMailSetting() != null) {
|
||||
EmailAddress systemAddress = EmailAddress.parse(mailSetting.getEmailAddress());
|
||||
ParsedEmailAddress systemAddress = ParsedEmailAddress.parse(mailSetting.getEmailAddress());
|
||||
return systemAddress.getSubAddressed("issue~" + issue.getId());
|
||||
} else {
|
||||
return null;
|
||||
@ -865,7 +889,7 @@ public class DefaultMailManager implements MailManager {
|
||||
public String getReplyAddress(PullRequest request) {
|
||||
MailSetting mailSetting = settingManager.getMailSetting();
|
||||
if (mailSetting != null && mailSetting.getReceiveMailSetting() != null) {
|
||||
EmailAddress systemAddress = EmailAddress.parse(mailSetting.getEmailAddress());
|
||||
ParsedEmailAddress systemAddress = ParsedEmailAddress.parse(mailSetting.getEmailAddress());
|
||||
return systemAddress.getSubAddressed("pullrequest~" + request.getId());
|
||||
} else {
|
||||
return null;
|
||||
@ -876,7 +900,7 @@ public class DefaultMailManager implements MailManager {
|
||||
public String getUnsubscribeAddress(Issue issue) {
|
||||
MailSetting mailSetting = settingManager.getMailSetting();
|
||||
if (mailSetting != null && mailSetting.getReceiveMailSetting() != null) {
|
||||
EmailAddress systemAddress = EmailAddress.parse(mailSetting.getEmailAddress());
|
||||
ParsedEmailAddress systemAddress = ParsedEmailAddress.parse(mailSetting.getEmailAddress());
|
||||
return systemAddress.getSubAddressed("issueunsubscribe~" + issue.getId());
|
||||
} else {
|
||||
return null;
|
||||
@ -887,7 +911,7 @@ public class DefaultMailManager implements MailManager {
|
||||
public String getUnsubscribeAddress(PullRequest request) {
|
||||
MailSetting mailSetting = settingManager.getMailSetting();
|
||||
if (mailSetting != null && mailSetting.getReceiveMailSetting() != null) {
|
||||
EmailAddress systemAddress = EmailAddress.parse(mailSetting.getEmailAddress());
|
||||
ParsedEmailAddress systemAddress = ParsedEmailAddress.parse(mailSetting.getEmailAddress());
|
||||
return systemAddress.getSubAddressed("pullrequestunsubscribe~" + request.getId());
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@ -28,6 +28,7 @@ import io.onedev.server.event.issue.IssueOpened;
|
||||
import io.onedev.server.infomanager.UserInfoManager;
|
||||
import io.onedev.server.markdown.MarkdownManager;
|
||||
import io.onedev.server.markdown.MentionParser;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Group;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueWatch;
|
||||
@ -156,11 +157,14 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
String threadingReferences = String.format("<you-in-field-%s-%s@onedev>", entry.getKey(), issue.getUUID());
|
||||
for (User member: entry.getValue().getMembers()) {
|
||||
if (!member.equals(user)) {
|
||||
mailManager.sendMailAsync(Sets.newHashSet(member.getEmail()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
EmailAddress emailAddress = member.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Sets.newHashSet(emailAddress.getValue()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,11 +179,14 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
String threadingReferences = String.format("<you-in-field-%s-%s@onedev>", entry.getKey(), issue.getUUID());
|
||||
for (User member: entry.getValue()) {
|
||||
if (!member.equals(user)) {
|
||||
mailManager.sendMailAsync(Sets.newHashSet(member.getEmail()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
EmailAddress emailAddress = member.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Sets.newHashSet(emailAddress.getValue()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,15 +206,18 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
User mentionedUser = userManager.findByName(userName);
|
||||
if (mentionedUser != null) {
|
||||
issueWatchManager.watch(issue, mentionedUser, true);
|
||||
if (!notifiedEmailAddresses.stream().anyMatch(mentionedUser.getEmails()::contains)) {
|
||||
if (!isNotified(notifiedEmailAddresses, mentionedUser)) {
|
||||
String subject = String.format("[Issue %s] (Mentioned You) %s", issue.getFQN(), issue.getTitle());
|
||||
String threadingReferences = String.format("<mentioned-%s@onedev>", issue.getUUID());
|
||||
|
||||
mailManager.sendMailAsync(Sets.newHashSet(mentionedUser.getEmail()),
|
||||
Sets.newHashSet(), Sets.newHashSet(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
EmailAddress emailAddress = mentionedUser.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Sets.newHashSet(emailAddress.getValue()),
|
||||
Sets.newHashSet(), Sets.newHashSet(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
notifiedUsers.add(mentionedUser);
|
||||
}
|
||||
}
|
||||
@ -216,19 +226,21 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
|
||||
if (!(event instanceof IssueChanged)
|
||||
|| !(((IssueChanged) event).getChange().getData() instanceof ReferencedFromAware)) {
|
||||
Collection<User> bccUsers = new HashSet<>();
|
||||
Collection<String> bccEmailAddresses = new HashSet<>();
|
||||
|
||||
for (IssueWatch watch: issue.getWatches()) {
|
||||
Date visitDate = userInfoManager.getIssueVisitDate(watch.getUser(), issue);
|
||||
if (watch.isWatching()
|
||||
&& (visitDate == null || visitDate.before(event.getDate()))
|
||||
&& !notifiedUsers.contains(watch.getUser())
|
||||
&& !notifiedEmailAddresses.stream().anyMatch(watch.getUser().getEmails()::contains)) {
|
||||
bccUsers.add(watch.getUser());
|
||||
&& !isNotified(notifiedEmailAddresses, watch.getUser())) {
|
||||
EmailAddress emailAddress = watch.getUser().getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
bccEmailAddresses.add(emailAddress.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!bccUsers.isEmpty()) {
|
||||
if (!bccEmailAddresses.isEmpty()) {
|
||||
String subject = String.format("[Issue %s] (%s) %s",
|
||||
issue.getFQN(), (event instanceof IssueOpened)?"Opened":"Updated", issue.getTitle());
|
||||
|
||||
@ -238,10 +250,10 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
|
||||
String threadingReferences = issue.getEffectiveThreadingReference();
|
||||
mailManager.sendMailAsync(Sets.newHashSet(), Sets.newHashSet(),
|
||||
bccUsers.stream().map(User::getEmail).collect(Collectors.toList()),
|
||||
subject, htmlBody, textBody, replyAddress, threadingReferences);
|
||||
bccEmailAddresses, subject, htmlBody, textBody,
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ import io.onedev.server.event.pullrequest.PullRequestUpdated;
|
||||
import io.onedev.server.infomanager.UserInfoManager;
|
||||
import io.onedev.server.markdown.MarkdownManager;
|
||||
import io.onedev.server.markdown.MentionParser;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.PullRequestAssignment;
|
||||
import io.onedev.server.model.PullRequestReview;
|
||||
@ -201,11 +202,14 @@ public class PullRequestNotificationManager extends AbstractNotificationManager
|
||||
WordUtils.capitalize(changeData.getActivity()), request.getTitle());
|
||||
String threadingReferences = String.format("<%s-%s@onedev>",
|
||||
changeData.getActivity().replace(' ', '-'), request.getUUID());
|
||||
mailManager.sendMailAsync(Lists.newArrayList(request.getSubmitter().getEmail()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
EmailAddress emailAddress = request.getSubmitter().getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Lists.newArrayList(emailAddress.getValue()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
notifiedUsers.add(request.getSubmitter());
|
||||
}
|
||||
} else if (changeData instanceof PullRequestAssigneeAddData) {
|
||||
@ -232,11 +236,14 @@ public class PullRequestNotificationManager extends AbstractNotificationManager
|
||||
assignmentSummary = user.getDisplayName() + " assigned to you";
|
||||
else
|
||||
assignmentSummary = "Assigned to you";
|
||||
mailManager.sendMailAsync(Lists.newArrayList(assignee.getEmail()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, assignmentSummary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, assignmentSummary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
EmailAddress emailAddress = assignee.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Lists.newArrayList(emailAddress.getValue()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, assignmentSummary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, assignmentSummary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
notifiedUsers.add(assignee);
|
||||
}
|
||||
}
|
||||
@ -252,11 +259,15 @@ public class PullRequestNotificationManager extends AbstractNotificationManager
|
||||
reviewInvitationSummary = user.getDisplayName() + " invited you to review";
|
||||
else
|
||||
reviewInvitationSummary = "Invited you to review";
|
||||
mailManager.sendMailAsync(Lists.newArrayList(reviewer.getEmail()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, reviewInvitationSummary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, reviewInvitationSummary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
|
||||
EmailAddress emailAddress = reviewer.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Lists.newArrayList(emailAddress.getValue()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), subject,
|
||||
getHtmlBody(event, reviewInvitationSummary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, reviewInvitationSummary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
notifiedUsers.add(reviewer);
|
||||
}
|
||||
}
|
||||
@ -272,15 +283,18 @@ public class PullRequestNotificationManager extends AbstractNotificationManager
|
||||
User mentionedUser = userManager.findByName(userName);
|
||||
if (mentionedUser != null) {
|
||||
pullRequestWatchManager.watch(request, mentionedUser, true);
|
||||
if (!notifiedEmailAddresses.stream().anyMatch(mentionedUser.getEmails()::contains)) {
|
||||
if (!isNotified(notifiedEmailAddresses, mentionedUser)) {
|
||||
String subject = String.format("[Pull Request %s] (Mentioned You) %s", request.getFQN(), request.getTitle());
|
||||
String threadingReferences = String.format("<mentioned-%s@onedev>", request.getUUID());
|
||||
|
||||
mailManager.sendMailAsync(Sets.newHashSet(mentionedUser.getEmail()),
|
||||
Sets.newHashSet(), Sets.newHashSet(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
EmailAddress emailAddress = mentionedUser.getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
mailManager.sendMailAsync(Sets.newHashSet(emailAddress.getValue()),
|
||||
Sets.newHashSet(), Sets.newHashSet(), subject,
|
||||
getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, null),
|
||||
getTextBody(event, summary, event.getTextBody(), url, replyable, null),
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
notifiedUsers.add(mentionedUser);
|
||||
}
|
||||
}
|
||||
@ -302,7 +316,7 @@ public class PullRequestNotificationManager extends AbstractNotificationManager
|
||||
}
|
||||
|
||||
if (notifyWatchers) {
|
||||
Collection<User> bccUsers = new HashSet<>();
|
||||
Collection<String> bccEmailAddresses = new HashSet<>();
|
||||
|
||||
for (PullRequestWatch watch: request.getWatches()) {
|
||||
Date visitDate = userInfoManager.getPullRequestVisitDate(watch.getUser(), request);
|
||||
@ -310,12 +324,14 @@ public class PullRequestNotificationManager extends AbstractNotificationManager
|
||||
&& (visitDate == null || visitDate.before(event.getDate()))
|
||||
&& (!(event instanceof PullRequestUpdated) || !watch.getUser().equals(request.getSubmitter()))
|
||||
&& !notifiedUsers.contains(watch.getUser())
|
||||
&& !notifiedEmailAddresses.stream().anyMatch(watch.getUser().getEmails()::contains)) {
|
||||
bccUsers.add(watch.getUser());
|
||||
&& !isNotified(notifiedEmailAddresses, watch.getUser())) {
|
||||
EmailAddress emailAddress = watch.getUser().getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
bccEmailAddresses.add(emailAddress.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!bccUsers.isEmpty()) {
|
||||
if (!bccEmailAddresses.isEmpty()) {
|
||||
String subject = String.format("[Pull Request %s] (%s) %s",
|
||||
request.getFQN(), (event instanceof PullRequestOpened)?"Opened":"Updated", request.getTitle());
|
||||
String threadingReferences = "<" + request.getUUID() + "@onedev>";
|
||||
@ -324,8 +340,8 @@ public class PullRequestNotificationManager extends AbstractNotificationManager
|
||||
String textBody = getTextBody(event, summary, event.getTextBody(), url, replyable, unsubscribable);
|
||||
mailManager.sendMailAsync(
|
||||
Lists.newArrayList(), Lists.newArrayList(),
|
||||
bccUsers.stream().map(User::getEmail).collect(Collectors.toList()),
|
||||
subject, htmlBody, textBody, replyAddress, threadingReferences);
|
||||
bccEmailAddresses, subject, htmlBody, textBody,
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,197 @@
|
||||
package io.onedev.server.rest;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.rest.annotation.EntityCreate;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
@Api(order=5010)
|
||||
@Path("/email-addresses")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Singleton
|
||||
public class EmailAddressResource {
|
||||
|
||||
private final UserManager userManager;
|
||||
|
||||
private final EmailAddressManager emailAddressManager;
|
||||
|
||||
private final SettingManager settingManager;
|
||||
|
||||
@Inject
|
||||
public EmailAddressResource(UserManager userManager, EmailAddressManager emailAddressManager,
|
||||
SettingManager settingManager) {
|
||||
this.userManager = userManager;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
this.settingManager = settingManager;
|
||||
}
|
||||
|
||||
@Api(order=100)
|
||||
@Path("/{emailAddressId}")
|
||||
@GET
|
||||
public EmailAddress get(@PathParam("emailAddressId") Long emailAddressId) {
|
||||
EmailAddress emailAddress = emailAddressManager.load(emailAddressId);
|
||||
if (!SecurityUtils.isAdministrator() && !emailAddress.getOwner().equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
@Api(order=150)
|
||||
@Path("/{emailAddressId}/verified")
|
||||
@GET
|
||||
public boolean getVerified(@PathParam("emailAddressId") Long emailAddressId) {
|
||||
EmailAddress emailAddress = emailAddressManager.load(emailAddressId);
|
||||
if (!SecurityUtils.isAdministrator() && !emailAddress.getOwner().equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
return emailAddress.isVerified();
|
||||
}
|
||||
|
||||
@Api(order=200, description="Create new email address")
|
||||
@POST
|
||||
public Long create(@NotNull @Valid EmailAddressCreateData data) {
|
||||
User user = userManager.load(data.getOwnerId());
|
||||
if (!SecurityUtils.isAdministrator() && !user.equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
|
||||
if (emailAddressManager.findByValue(data.getValue()) != null)
|
||||
throw new ExplicitException("This email address is already used by another user");
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setOwner(userManager.load(data.getOwnerId()));
|
||||
emailAddress.setValue(data.getValue());
|
||||
|
||||
if (SecurityUtils.isAdministrator())
|
||||
emailAddress.setVerificationCode(null);
|
||||
|
||||
emailAddressManager.save(emailAddress);
|
||||
return emailAddress.getId();
|
||||
}
|
||||
|
||||
@Api(order=250, description="Set as primary email address")
|
||||
@Path("/primary")
|
||||
@POST
|
||||
public Long setAsPrimary(@NotNull Long emailAddressId) {
|
||||
EmailAddress emailAddress = emailAddressManager.load(emailAddressId);
|
||||
|
||||
if (!SecurityUtils.isAdministrator() && !emailAddress.getOwner().equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
|
||||
if (emailAddress.getOwner().isExternalManaged())
|
||||
throw new ExplicitException("Can not set primary email address for externally authenticated user");
|
||||
|
||||
emailAddressManager.setAsPrimary(emailAddress);
|
||||
|
||||
return emailAddressId;
|
||||
}
|
||||
|
||||
@Api(order=260, description="Use for git operations")
|
||||
@Path("/git")
|
||||
@POST
|
||||
public Long useForGitOperations(@NotNull Long emailAddressId) {
|
||||
EmailAddress emailAddress = emailAddressManager.load(emailAddressId);
|
||||
|
||||
if (!SecurityUtils.isAdministrator() && !emailAddress.getOwner().equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
|
||||
emailAddressManager.useForGitOperations(emailAddress);
|
||||
|
||||
return emailAddressId;
|
||||
}
|
||||
|
||||
@Api(order=260, description="Resend verification email")
|
||||
@Path("/resend-verification-email")
|
||||
@POST
|
||||
public Long resendVerificationEmail(@NotNull Long emailAddressId) {
|
||||
EmailAddress emailAddress = emailAddressManager.load(emailAddressId);
|
||||
|
||||
if (!SecurityUtils.isAdministrator() && !emailAddress.getOwner().equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
|
||||
if (settingManager.getMailSetting() == null)
|
||||
throw new ExplicitException("Unable to send verification email as mail setting is not specified");
|
||||
if (emailAddress.isVerified())
|
||||
throw new ExplicitException("Unable to send verification email as this email address is already verified");
|
||||
|
||||
emailAddressManager.sendVerificationEmail(emailAddress);
|
||||
|
||||
return emailAddressId;
|
||||
}
|
||||
|
||||
@Api(order=300)
|
||||
@Path("/{emailAddressId}")
|
||||
@DELETE
|
||||
public Response delete(@PathParam("emailAddressId") Long emailAddressId) {
|
||||
EmailAddress emailAddress = emailAddressManager.load(emailAddressId);
|
||||
if (SecurityUtils.isAdministrator()
|
||||
|| emailAddress.getOwner().equals(SecurityUtils.getUser())) {
|
||||
if (emailAddress.isPrimary() && emailAddress.getOwner().isExternalManaged()) {
|
||||
throw new ExplicitException("Can not delete primary email address of "
|
||||
+ "externally authenticated user");
|
||||
}
|
||||
if (emailAddress.getOwner().getEmailAddresses().size() == 1)
|
||||
throw new ExplicitException("At least one email address should be present for a user");
|
||||
emailAddressManager.delete(emailAddress);
|
||||
return Response.ok().build();
|
||||
} else {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
}
|
||||
|
||||
@EntityCreate(EmailAddress.class)
|
||||
public static class EmailAddressCreateData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long ownerId;
|
||||
|
||||
private String value;
|
||||
|
||||
@Api(order=100, description="Id of user owning this email address")
|
||||
@NotNull
|
||||
public Long getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setOwnerId(Long ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
@Api(order=200, description="The email address string")
|
||||
@NotEmpty
|
||||
@Email
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,7 +20,7 @@ import io.onedev.server.model.IssueQueryPersonalization;
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
@Api(order=5100)
|
||||
@Api(order=5150)
|
||||
@Path("/issue-query-personalizations")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
||||
@ -203,8 +203,8 @@ public class RepositoryResource {
|
||||
if (project.getTagRef(request.getTagName()) != null) {
|
||||
throw new InvalidParamException("Tag '" + request.getTagName() + "' already exists");
|
||||
} else {
|
||||
PersonIdent tagger = SecurityUtils.getUser().asPerson();
|
||||
project.createTag(request.getTagName(), request.getRevision(), tagger, request.getTagMessage());
|
||||
project.createTag(request.getTagName(), request.getRevision(),
|
||||
SecurityUtils.getUser().asPerson(), request.getTagMessage());
|
||||
}
|
||||
|
||||
return Response.ok().build();
|
||||
|
||||
@ -21,7 +21,7 @@ import io.onedev.server.model.SshKey;
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
@Api(order=5100)
|
||||
@Api(order=5050)
|
||||
@Path("/ssh-keys")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
||||
@ -7,10 +7,10 @@ import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
@ -26,18 +26,17 @@ import javax.ws.rs.core.Response;
|
||||
import org.apache.shiro.authc.credential.PasswordService;
|
||||
import org.apache.shiro.authz.UnauthenticatedException;
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
import org.hibernate.criterion.MatchMode;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.SshKeyManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.BuildQueryPersonalization;
|
||||
import io.onedev.server.model.CodeCommentQueryPersonalization;
|
||||
import io.onedev.server.model.CommitQueryPersonalization;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.IssueQueryPersonalization;
|
||||
import io.onedev.server.model.IssueVote;
|
||||
import io.onedev.server.model.IssueWatch;
|
||||
@ -50,15 +49,15 @@ import io.onedev.server.model.SshKey;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.UserAuthorization;
|
||||
import io.onedev.server.model.support.NamedProjectQuery;
|
||||
import io.onedev.server.model.support.SsoInfo;
|
||||
import io.onedev.server.model.support.build.NamedBuildQuery;
|
||||
import io.onedev.server.model.support.issue.NamedIssueQuery;
|
||||
import io.onedev.server.model.support.pullrequest.NamedPullRequestQuery;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.rest.annotation.EntityCreate;
|
||||
import io.onedev.server.rest.exception.InvalidParamException;
|
||||
import io.onedev.server.rest.support.RestConstants;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.validation.annotation.UserName;
|
||||
|
||||
@Api(order=5000)
|
||||
@Path("/users")
|
||||
@ -73,17 +72,21 @@ public class UserResource {
|
||||
|
||||
private final PasswordService passwordService;
|
||||
|
||||
private final EmailAddressManager emailAddressManager;
|
||||
|
||||
@Inject
|
||||
public UserResource(UserManager userManager, SshKeyManager sshKeyManager, PasswordService passwordService) {
|
||||
public UserResource(UserManager userManager, SshKeyManager sshKeyManager,
|
||||
PasswordService passwordService, EmailAddressManager emailAddressManager) {
|
||||
this.userManager = userManager;
|
||||
this.sshKeyManager = sshKeyManager;
|
||||
this.passwordService = passwordService;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
}
|
||||
|
||||
@Api(order=100)
|
||||
@Path("/{userId}")
|
||||
@GET
|
||||
public User getBasicInfo(@PathParam("userId") Long userId) {
|
||||
public User getProfile(@PathParam("userId") Long userId) {
|
||||
User user = userManager.load(userId);
|
||||
if (!SecurityUtils.isAdministrator() && !user.equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
@ -93,13 +96,33 @@ public class UserResource {
|
||||
@Api(order=200)
|
||||
@Path("/me")
|
||||
@GET
|
||||
public User getMyBasicInfo() {
|
||||
public User getMyProfile() {
|
||||
User user = SecurityUtils.getUser();
|
||||
if (user == null)
|
||||
throw new UnauthenticatedException();
|
||||
return user;
|
||||
}
|
||||
|
||||
@Api(order=250)
|
||||
@Path("/{userId}/access-token")
|
||||
@GET
|
||||
public String getAccessToken(@PathParam("userId") Long userId) {
|
||||
User user = userManager.load(userId);
|
||||
if (!SecurityUtils.isAdministrator() && !user.equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
return user.getAccessToken();
|
||||
}
|
||||
|
||||
@Api(order=275)
|
||||
@Path("/{userId}/email-addresses")
|
||||
@GET
|
||||
public Collection<EmailAddress> getEmailAddresses(@PathParam("userId") Long userId) {
|
||||
User user = userManager.load(userId);
|
||||
if (!SecurityUtils.isAdministrator() && !user.equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
return user.getEmailAddresses();
|
||||
}
|
||||
|
||||
@Api(order=300)
|
||||
@Path("/{userId}/authorizations")
|
||||
@GET
|
||||
@ -118,16 +141,6 @@ public class UserResource {
|
||||
return userManager.load(userId).getMemberships();
|
||||
}
|
||||
|
||||
@Api(order=500)
|
||||
@Path("/{userId}/sso-info")
|
||||
@GET
|
||||
public SsoInfo getSsoInfo(@PathParam("userId") Long userId) {
|
||||
User user = userManager.load(userId);
|
||||
if (!SecurityUtils.isAdministrator() && !user.equals(SecurityUtils.getUser()))
|
||||
throw new UnauthorizedException();
|
||||
return user.getSsoInfo();
|
||||
}
|
||||
|
||||
@Api(order=600)
|
||||
@Path("/{userId}/pull-request-reviews")
|
||||
@GET
|
||||
@ -258,8 +271,9 @@ public class UserResource {
|
||||
|
||||
@Api(order=1800)
|
||||
@GET
|
||||
public List<User> queryBasicInfo(@QueryParam("name") String name, @QueryParam("fullName") String fullName,
|
||||
@QueryParam("email") String email, @QueryParam("offset") @Api(example="0") int offset,
|
||||
public List<User> queryProfile(
|
||||
@QueryParam("term") @Api(description="Any string in login name, full name or email address") String term,
|
||||
@QueryParam("offset") @Api(example="0") int offset,
|
||||
@QueryParam("count") @Api(example="100") int count) {
|
||||
|
||||
if (!SecurityUtils.isAdministrator())
|
||||
@ -268,51 +282,56 @@ public class UserResource {
|
||||
if (count > RestConstants.MAX_PAGE_SIZE)
|
||||
throw new InvalidParamException("Count should not be greater than " + RestConstants.MAX_PAGE_SIZE);
|
||||
|
||||
EntityCriteria<User> criteria = EntityCriteria.of(User.class);
|
||||
criteria.add(Restrictions.gt("id", 0L));
|
||||
if (name != null)
|
||||
criteria.add(Restrictions.ilike("name", name.replace('*', '%'), MatchMode.EXACT));
|
||||
if (fullName != null)
|
||||
criteria.add(Restrictions.ilike("fullName", fullName.replace('*', '%'), MatchMode.EXACT));
|
||||
if (email != null)
|
||||
criteria.add(Restrictions.ilike("email", email.replace('*', '%'), MatchMode.EXACT));
|
||||
|
||||
return userManager.query(criteria, offset, count);
|
||||
return userManager.query(term, offset, count);
|
||||
}
|
||||
|
||||
@Api(order=1900, description="Update user of specified id in request body, or create new if id property not provided")
|
||||
@Api(order=1900, description="Create new user")
|
||||
@POST
|
||||
public Long createOrUpdate(@NotNull User user) {
|
||||
if (user.isNew()) {
|
||||
if (!SecurityUtils.isAdministrator()) {
|
||||
throw new UnauthorizedException();
|
||||
} else {
|
||||
user.setPassword("impossible_password");
|
||||
checkEmails(user);
|
||||
userManager.save(user);
|
||||
}
|
||||
} else {
|
||||
if (!SecurityUtils.isAdministrator() && !user.equals(SecurityUtils.getUser())) {
|
||||
throw new UnauthorizedException();
|
||||
} else {
|
||||
checkEmails(user);
|
||||
userManager.save(user, (String) user.getCustomData());
|
||||
}
|
||||
}
|
||||
return user.getId();
|
||||
public Long create(@NotNull @Valid UserCreateData data) {
|
||||
if (SecurityUtils.isAdministrator()) {
|
||||
if (userManager.findByName(data.getName()) != null)
|
||||
throw new ExplicitException("Login name is already used by another user");
|
||||
if (emailAddressManager.findByValue(data.getEmailAddress()) != null)
|
||||
throw new ExplicitException("Email address is already used by another user");
|
||||
|
||||
User user = new User();
|
||||
user.setName(data.getName());
|
||||
user.setFullName(data.getFullName());
|
||||
user.setPassword(passwordService.encryptPassword(data.getPassword()));
|
||||
userManager.save(user);
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setGit(true);
|
||||
emailAddress.setPrimary(true);
|
||||
emailAddress.setOwner(user);
|
||||
emailAddress.setValue(data.getEmailAddress());
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddressManager.save(emailAddress);
|
||||
|
||||
return user.getId();
|
||||
} else {
|
||||
throw new UnauthenticatedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkEmails(User user) {
|
||||
Set<String> emails = Sets.newHashSet(user.getEmail());
|
||||
if (user.getGitEmail() != null)
|
||||
emails.add(user.getGitEmail());
|
||||
emails.addAll(user.getAlternateEmails());
|
||||
for (String email: emails) {
|
||||
User userWithSameEmail = userManager.findByEmail(email);
|
||||
if (userWithSameEmail != null && !userWithSameEmail.equals(user))
|
||||
throw new ExplicitException("Email '" + email + "' already used by another user.");
|
||||
@Api(order=1950, description="Update user profile")
|
||||
@Path("/{userId}")
|
||||
@POST
|
||||
public Long updateProfile(@PathParam("userId") Long userId, @NotNull @Valid ProfileUpdateData data) {
|
||||
User user = userManager.load(userId);
|
||||
if (SecurityUtils.isAdministrator() || user.equals(SecurityUtils.getUser())) {
|
||||
User existingUser = userManager.findByName(data.getName());
|
||||
if (existingUser != null && !existingUser.equals(user))
|
||||
throw new ExplicitException("Login name is already used by another user");
|
||||
|
||||
user.setName(data.getName());
|
||||
user.setFullName(data.getFullName());
|
||||
userManager.save(user);
|
||||
return user.getId();
|
||||
} else {
|
||||
throw new UnauthenticatedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Api(order=2000)
|
||||
@Path("/{userId}/password")
|
||||
@ -322,9 +341,9 @@ public class UserResource {
|
||||
if (!SecurityUtils.isAdministrator() && !user.equals(SecurityUtils.getUser())) {
|
||||
throw new UnauthorizedException();
|
||||
} else if (user.getPassword().equals(User.EXTERNAL_MANAGED)) {
|
||||
if (user.getSsoInfo().getConnector() != null) {
|
||||
if (user.getSsoConnector() != null) {
|
||||
throw new ExplicitException("The user is currently authenticated via SSO provider '"
|
||||
+ user.getSsoInfo().getConnector() + "', please change password there instead");
|
||||
+ user.getSsoConnector() + "', please change password there instead");
|
||||
} else {
|
||||
throw new ExplicitException("The user is currently authenticated via external system, "
|
||||
+ "please change password there instead");
|
||||
@ -336,18 +355,6 @@ public class UserResource {
|
||||
}
|
||||
}
|
||||
|
||||
@Api(order=2100)
|
||||
@Path("/{userId}/sso-info")
|
||||
@POST
|
||||
public Response setSsoInfo(@PathParam("userId") Long userId, @NotNull SsoInfo ssoInfo) {
|
||||
if (!SecurityUtils.isAdministrator())
|
||||
throw new UnauthorizedException();
|
||||
User user = userManager.load(userId);
|
||||
user.setSsoInfo(ssoInfo);
|
||||
userManager.save(user);
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@Api(order=2100)
|
||||
@Path("/{userId}/queries-and-watches")
|
||||
@POST
|
||||
@ -398,6 +405,92 @@ public class UserResource {
|
||||
userManager.delete(user);
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@EntityCreate(User.class)
|
||||
public static class UserCreateData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
|
||||
private String password;
|
||||
|
||||
private String fullName;
|
||||
|
||||
private String emailAddress;
|
||||
|
||||
@Api(order=100, description="Login name of the user")
|
||||
@UserName
|
||||
@NotEmpty
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Api(order=150)
|
||||
@NotEmpty
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Api(order=200)
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
@Api(order=300)
|
||||
@Email
|
||||
@NotEmpty
|
||||
public String getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
public void setEmailAddress(String emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ProfileUpdateData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
|
||||
private String fullName;
|
||||
|
||||
@Api(order=100, description="Login name of the user")
|
||||
@UserName
|
||||
@NotEmpty
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Api(order=200)
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class QueriesAndWatches implements Serializable {
|
||||
|
||||
|
||||
@ -32,11 +32,9 @@ public class AuthorCriteria extends CommitCriteria {
|
||||
if (value == null) { // authored by me
|
||||
User user = SecurityUtils.getUser();
|
||||
if (user != null) {
|
||||
command.authors().add("<" + user.getEmail() + ">");
|
||||
if (user.getGitEmail() != null)
|
||||
command.authors().add("<" + user.getGitEmail() + ">");
|
||||
for (String email: user.getAlternateEmails())
|
||||
command.authors().add("<" + email + ">");
|
||||
user.getEmailAddresses().stream().filter(it->it.isVerified()).forEach(it-> {
|
||||
command.authors().add("<" + it.getValue() + ">");
|
||||
});
|
||||
} else {
|
||||
throw new ExplicitException("Please login to perform this query");
|
||||
}
|
||||
@ -55,9 +53,8 @@ public class AuthorCriteria extends CommitCriteria {
|
||||
User user = User.get();
|
||||
if (user == null) {
|
||||
throw new ExplicitException("Please login to perform this query");
|
||||
} else if (user.getEmail().equals(authorEmail)
|
||||
|| user.getGitEmail()!=null && user.getGitEmail().equals(authorEmail)
|
||||
|| user.getAlternateEmails().contains(authorEmail)) {
|
||||
} else if (user.getEmailAddresses().stream()
|
||||
.anyMatch(it-> it.isVerified() && it.getValue().equalsIgnoreCase(authorEmail))) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -32,11 +32,9 @@ public class CommitterCriteria extends CommitCriteria {
|
||||
if (value == null) { // committed by me
|
||||
User user = SecurityUtils.getUser();
|
||||
if (user != null) {
|
||||
command.committers().add("<" + user.getEmail() + ">");
|
||||
if (user.getGitEmail() != null)
|
||||
command.committers().add("<" + user.getGitEmail() + ">");
|
||||
for (String email: user.getAlternateEmails())
|
||||
command.committers().add("<" + email + ">");
|
||||
user.getEmailAddresses().stream().filter(it->it.isVerified()).forEach(it-> {
|
||||
command.committers().add("<" + it.getValue() + ">");
|
||||
});
|
||||
} else {
|
||||
throw new ExplicitException("Please login to perform this query");
|
||||
}
|
||||
@ -55,9 +53,8 @@ public class CommitterCriteria extends CommitCriteria {
|
||||
User user = User.get();
|
||||
if (user == null) {
|
||||
throw new ExplicitException("Please login to perform this query");
|
||||
} else if (user.getEmail().equals(committerEmail)
|
||||
|| user.getGitEmail() != null && user.getGitEmail().equals(committerEmail)
|
||||
|| user.getAlternateEmails().contains(committerEmail)) {
|
||||
} else if (user.getEmailAddresses().stream()
|
||||
.anyMatch(it-> it.isVerified() && it.getValue().equalsIgnoreCase(committerEmail))) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -16,6 +16,7 @@ import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.wicket.MetaDataKey;
|
||||
import org.apache.wicket.request.cycle.RequestCycle;
|
||||
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.GroupManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
@ -35,6 +36,8 @@ public abstract class AbstractAuthorizingRealm extends AuthorizingRealm {
|
||||
|
||||
protected final UserManager userManager;
|
||||
|
||||
protected final EmailAddressManager emailAddressManager;
|
||||
|
||||
protected final GroupManager groupManager;
|
||||
|
||||
protected final ProjectManager projectManager;
|
||||
@ -50,12 +53,13 @@ public abstract class AbstractAuthorizingRealm extends AuthorizingRealm {
|
||||
@Inject
|
||||
public AbstractAuthorizingRealm(UserManager userManager, GroupManager groupManager,
|
||||
ProjectManager projectManager, SessionManager sessionManager,
|
||||
SettingManager settingManager) {
|
||||
SettingManager settingManager, EmailAddressManager emailAddressManager) {
|
||||
this.userManager = userManager;
|
||||
this.groupManager = groupManager;
|
||||
this.projectManager = projectManager;
|
||||
this.sessionManager = sessionManager;
|
||||
this.settingManager = settingManager;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
}
|
||||
|
||||
private Collection<Permission> getGroupPermissions(Group group, User user) {
|
||||
|
||||
@ -8,6 +8,7 @@ import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
|
||||
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.GroupManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
@ -21,8 +22,9 @@ public class BearerAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
@Inject
|
||||
public BearerAuthorizingRealm(UserManager userManager, GroupManager groupManager,
|
||||
ProjectManager projectManager, SessionManager sessionManager,
|
||||
SettingManager settingManager) {
|
||||
super(userManager, groupManager, projectManager, sessionManager, settingManager);
|
||||
SettingManager settingManager, EmailAddressManager emailAddressManager) {
|
||||
super(userManager, groupManager, projectManager, sessionManager,
|
||||
settingManager, emailAddressManager);
|
||||
setCredentialsMatcher(new AllowAllCredentialsMatcher());
|
||||
}
|
||||
|
||||
|
||||
@ -20,12 +20,14 @@ import org.slf4j.LoggerFactory;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.commons.utils.ExceptionUtils;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.GroupManager;
|
||||
import io.onedev.server.entitymanager.MembershipManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.SshKeyManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.administration.authenticator.Authenticated;
|
||||
import io.onedev.server.model.support.administration.authenticator.Authenticator;
|
||||
@ -48,8 +50,9 @@ public class PasswordAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
MembershipManager membershipManager, GroupManager groupManager,
|
||||
ProjectManager projectManager, SessionManager sessionManager,
|
||||
TransactionManager transactionManager, SshKeyManager sshKeyManager,
|
||||
PasswordService passwordService) {
|
||||
super(userManager, groupManager, projectManager, sessionManager, settingManager);
|
||||
PasswordService passwordService, EmailAddressManager emailAddressManager) {
|
||||
super(userManager, groupManager, projectManager, sessionManager,
|
||||
settingManager, emailAddressManager);
|
||||
|
||||
PasswordMatcher passwordMatcher = new PasswordMatcher();
|
||||
passwordMatcher.setPasswordService(passwordService);
|
||||
@ -69,11 +72,19 @@ public class PasswordAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
User user = new User();
|
||||
user.setName(userName);
|
||||
user.setPassword(User.EXTERNAL_MANAGED);
|
||||
user.setEmail(authenticated.getEmail());
|
||||
if (authenticated.getFullName() != null)
|
||||
user.setFullName(authenticated.getFullName());
|
||||
|
||||
userManager.save(user);
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setValue(authenticated.getEmail());
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddress.setOwner(user);
|
||||
emailAddress.setPrimary(true);
|
||||
emailAddress.setGit(true);
|
||||
emailAddressManager.save(emailAddress);
|
||||
|
||||
user.getEmailAddresses().add(emailAddress);
|
||||
|
||||
Collection<String> groupNames = authenticated.getGroupNames();
|
||||
if (groupNames == null && defaultGroup != null)
|
||||
@ -86,14 +97,26 @@ public class PasswordAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
return user;
|
||||
}
|
||||
|
||||
private void updateUser(User user, Authenticated authenticated) {
|
||||
user.setEmail(authenticated.getEmail());
|
||||
private void updateUser(User user, Authenticated authenticated, @Nullable EmailAddress emailAddress) {
|
||||
if (emailAddress != null) {
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddressManager.setAsPrimary(emailAddress);
|
||||
} else {
|
||||
emailAddress = new EmailAddress();
|
||||
emailAddress.setValue(authenticated.getEmail());
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddress.setOwner(user);
|
||||
emailAddress.setPrimary(true);
|
||||
emailAddress.setGit(true);
|
||||
emailAddressManager.save(emailAddress);
|
||||
}
|
||||
user.setSsoConnector(null);
|
||||
if (authenticated.getFullName() != null)
|
||||
user.setFullName(authenticated.getFullName());
|
||||
userManager.save(user);
|
||||
|
||||
if (authenticated.getGroupNames() != null)
|
||||
membershipManager.syncMemberships(user, authenticated.getGroupNames());
|
||||
|
||||
if (authenticated.getSshKeys() != null)
|
||||
sshKeyManager.syncSshKeys(user, authenticated.getSshKeys());
|
||||
}
|
||||
@ -106,44 +129,54 @@ public class PasswordAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
@Override
|
||||
public AuthenticationInfo call() {
|
||||
try {
|
||||
String userName = (String) token.getPrincipal();
|
||||
User user = userManager.findByName(userName);
|
||||
if (user == null)
|
||||
user = userManager.findByEmail(userName);
|
||||
if (user == null) {
|
||||
String userNameOrEmailAddressValue = (String) token.getPrincipal();
|
||||
User user;
|
||||
EmailAddress emailAddress = emailAddressManager.findByValue(userNameOrEmailAddressValue);
|
||||
if (emailAddress != null)
|
||||
user = emailAddress.getOwner();
|
||||
else
|
||||
user = userManager.findByName(userNameOrEmailAddressValue);
|
||||
if (user != null) {
|
||||
if (user.isExternalManaged()) {
|
||||
Authenticator authenticator = settingManager.getAuthenticator();
|
||||
if (authenticator != null) {
|
||||
UsernamePasswordToken authToken = (UsernamePasswordToken) token;
|
||||
authToken = new UsernamePasswordToken(user.getName(), authToken.getPassword(),
|
||||
authToken.isRememberMe(), authToken.getHost());
|
||||
Authenticated authenticated = authenticator.authenticate(authToken);
|
||||
String emailAddressValue = authenticated.getEmail();
|
||||
emailAddress = emailAddressManager.findByValue(emailAddressValue);
|
||||
if (emailAddress != null && !emailAddress.getOwner().equals(user)) {
|
||||
throw new AuthenticationException("Email address '" + emailAddressValue
|
||||
+ "' has already been used by another user");
|
||||
} else {
|
||||
updateUser(user, authenticated, emailAddress);
|
||||
return user;
|
||||
}
|
||||
} else {
|
||||
throw new AuthenticationException("No external authenticator to authenticate user '"
|
||||
+ userNameOrEmailAddressValue + "'");
|
||||
}
|
||||
} else {
|
||||
return user;
|
||||
}
|
||||
} else if (emailAddress == null) {
|
||||
Authenticator authenticator = settingManager.getAuthenticator();
|
||||
if (authenticator != null) {
|
||||
Authenticated authenticated = authenticator.authenticate((UsernamePasswordToken) token);
|
||||
String email = authenticated.getEmail();
|
||||
if (userManager.findByEmail(email) != null) {
|
||||
throw new AuthenticationException("Email '" + email
|
||||
+ "' has already been used by another account");
|
||||
String emailAddressValue = authenticated.getEmail();
|
||||
if (emailAddressManager.findByValue(emailAddressValue) != null) {
|
||||
throw new AuthenticationException("Email address '" + emailAddressValue
|
||||
+ "' has already been used by another user");
|
||||
} else {
|
||||
return newUser(userNameOrEmailAddressValue, authenticated, authenticator.getDefaultGroup());
|
||||
}
|
||||
user = newUser(userName, authenticated, authenticator.getDefaultGroup());
|
||||
} else {
|
||||
throw new UnknownAccountException("Unable to find account data for token [" + token + "] in realm [" + this + "]");
|
||||
throw new UnknownAccountException("Unknown user");
|
||||
}
|
||||
} else if (user.getPassword().equals(User.EXTERNAL_MANAGED)) {
|
||||
if (user.getSsoInfo().getConnector() != null) {
|
||||
throw new AuthenticationException("Account '" + userName
|
||||
+ "' is set to authenticate via " + User.AUTH_SOURCE_SSO_PROVIDER
|
||||
+ user.getSsoInfo().getConnector());
|
||||
}
|
||||
Authenticator authenticator = settingManager.getAuthenticator();
|
||||
if (authenticator != null) {
|
||||
Authenticated authenticated = authenticator.authenticate((UsernamePasswordToken) token);
|
||||
String email = authenticated.getEmail();
|
||||
if (!email.equals(user.getEmail()) && userManager.findByEmail(email) != null) {
|
||||
throw new AuthenticationException("Email '" + email
|
||||
+ "' has already been used by another account");
|
||||
}
|
||||
updateUser(user, authenticated);
|
||||
} else {
|
||||
throw new AuthenticationException("Account '" + userName + "' is set to authenticate "
|
||||
+ "externally but " + User.AUTH_SOURCE_EXTERNAL_AUTHENTICATOR + " is not defined");
|
||||
}
|
||||
}
|
||||
return user;
|
||||
} else {
|
||||
throw new UnknownAccountException("Unknown user");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof AuthenticationException) {
|
||||
logger.debug("Authentication not passed", e);
|
||||
|
||||
@ -13,14 +13,15 @@ import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.GroupManager;
|
||||
import io.onedev.server.entitymanager.MembershipManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.SshKeyManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.SsoInfo;
|
||||
import io.onedev.server.model.support.administration.sso.SsoAuthenticated;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
@ -38,8 +39,8 @@ public class SsoAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
public SsoAuthorizingRealm(UserManager userManager, MembershipManager membershipManager,
|
||||
GroupManager groupManager, ProjectManager projectManager, SessionManager sessionManager,
|
||||
TransactionManager transactionManager, SshKeyManager sshKeyManager,
|
||||
SettingManager settingManager) {
|
||||
super(userManager, groupManager, projectManager, sessionManager, settingManager);
|
||||
SettingManager settingManager, EmailAddressManager emailAddressManager) {
|
||||
super(userManager, groupManager, projectManager, sessionManager, settingManager, emailAddressManager);
|
||||
setCredentialsMatcher(new AllowAllCredentialsMatcher());
|
||||
|
||||
this.membershipManager = membershipManager;
|
||||
@ -50,14 +51,21 @@ public class SsoAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
private User newUser(SsoAuthenticated authenticated) {
|
||||
User user = new User();
|
||||
user.setName(authenticated.getUserName());
|
||||
user.setSsoConnector(authenticated.getConnector().getName());
|
||||
user.setPassword(User.EXTERNAL_MANAGED);
|
||||
user.setEmail(authenticated.getEmail());
|
||||
if (authenticated.getFullName() != null)
|
||||
user.setFullName(authenticated.getFullName());
|
||||
user.setSsoInfo(authenticated.getSsoInfo());
|
||||
|
||||
userManager.save(user);
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddress.setValue(authenticated.getEmail());
|
||||
emailAddress.setPrimary(true);
|
||||
emailAddress.setGit(true);
|
||||
emailAddressManager.save(emailAddress);
|
||||
|
||||
user.getEmailAddresses().add(emailAddress);
|
||||
|
||||
Collection<String> groupNames = authenticated.getGroupNames();
|
||||
if (groupNames == null && authenticated.getConnector().getDefaultGroup() != null)
|
||||
groupNames = Sets.newHashSet(authenticated.getConnector().getDefaultGroup());
|
||||
@ -69,12 +77,18 @@ public class SsoAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
return user;
|
||||
}
|
||||
|
||||
private void updateUser(User user, SsoAuthenticated authenticated) {
|
||||
private void updateUser(EmailAddress emailAddress, SsoAuthenticated authenticated) {
|
||||
User user = emailAddress.getOwner();
|
||||
user.setName(authenticated.getUserName());
|
||||
user.setEmail(authenticated.getEmail());
|
||||
user.setSsoConnector(authenticated.getConnector().getName());
|
||||
user.setPassword(User.EXTERNAL_MANAGED);
|
||||
if (authenticated.getFullName() != null)
|
||||
user.setFullName(authenticated.getFullName());
|
||||
userManager.save(user);
|
||||
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddressManager.setAsPrimary(emailAddress);
|
||||
|
||||
if (authenticated.getGroupNames() != null)
|
||||
membershipManager.syncMemberships(user, authenticated.getGroupNames());
|
||||
|
||||
@ -94,26 +108,23 @@ public class SsoAuthorizingRealm extends AbstractAuthorizingRealm {
|
||||
|
||||
@Override
|
||||
public AuthenticationInfo call() {
|
||||
User user;
|
||||
EmailAddress emailAddress;
|
||||
SsoAuthenticated authenticated = (SsoAuthenticated) token;
|
||||
String userName = authenticated.getUserName();
|
||||
String email = authenticated.getEmail();
|
||||
SsoInfo ssoInfo = authenticated.getSsoInfo();
|
||||
user = userManager.findBySsoInfo(ssoInfo);
|
||||
if (user == null) {
|
||||
String emailAddressValue = authenticated.getEmail();
|
||||
emailAddress = emailAddressManager.findByValue(emailAddressValue);
|
||||
if (emailAddress == null) {
|
||||
if (userManager.findByName(userName) != null)
|
||||
throw new AuthenticationException("Account '" + userName + "' already exists");
|
||||
if (userManager.findByEmail(email) != null)
|
||||
throw new AuthenticationException("Email '" + email + "' has already been used by another account");
|
||||
user = newUser(authenticated);
|
||||
} else {
|
||||
if (!userName.equals(user.getName()) && userManager.findByName(userName) != null)
|
||||
throw new AuthenticationException("Account '" + userName + "' already exists");
|
||||
if (!email.equals(user.getEmail()) && userManager.findByEmail(email) != null)
|
||||
throw new AuthenticationException("Email '" + email + "' has already been used by another account");
|
||||
updateUser(user, authenticated);
|
||||
}
|
||||
return user;
|
||||
throw new AuthenticationException("Login name '" + userName + "' already used by another user");
|
||||
else
|
||||
return newUser(authenticated);
|
||||
} else if (!userName.equalsIgnoreCase(emailAddress.getOwner().getName())
|
||||
&& userManager.findByName(userName) != null) {
|
||||
throw new AuthenticationException("Login name '" + userName + "' already used by another user");
|
||||
} else {
|
||||
updateUser(emailAddress, authenticated);
|
||||
return emailAddress.getOwner();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@ import java.io.Serializable;
|
||||
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
|
||||
public class EmailAddress implements Serializable {
|
||||
public class ParsedEmailAddress implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -12,7 +12,7 @@ public class EmailAddress implements Serializable {
|
||||
|
||||
private final String domain;
|
||||
|
||||
public EmailAddress(String name, String domain) {
|
||||
public ParsedEmailAddress(String name, String domain) {
|
||||
this.name = name;
|
||||
this.domain = domain;
|
||||
}
|
||||
@ -25,10 +25,10 @@ public class EmailAddress implements Serializable {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public static EmailAddress parse(String emailAddress) {
|
||||
public static ParsedEmailAddress parse(String emailAddress) {
|
||||
String name = StringUtils.substringBefore(emailAddress, "@");
|
||||
String domain = StringUtils.substringAfter(emailAddress, "@");
|
||||
return new EmailAddress(name, domain);
|
||||
return new ParsedEmailAddress(name, domain);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1,75 +0,0 @@
|
||||
package io.onedev.server.util.facade;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.util.NameAndEmail;
|
||||
|
||||
public class UserFacade extends EntityFacade {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String fullName;
|
||||
|
||||
private final String email;
|
||||
|
||||
private final String gitEmail;
|
||||
|
||||
private final List<String> alternateEmails;
|
||||
|
||||
public UserFacade(Long id, String name, @Nullable String fullName, String email,
|
||||
@Nullable String gitEmail, List<String> alternateEmails) {
|
||||
super(id);
|
||||
this.name = name;
|
||||
this.fullName = fullName;
|
||||
this.email = email;
|
||||
this.gitEmail = gitEmail;
|
||||
this.alternateEmails = alternateEmails;
|
||||
}
|
||||
|
||||
public UserFacade(User user) {
|
||||
this(user.getId(), user.getName(), user.getFullName(), user.getEmail(),
|
||||
user.getGitEmail(), user.getAlternateEmails());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
if (getFullName() != null)
|
||||
return getFullName();
|
||||
else
|
||||
return getName();
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getGitEmail() {
|
||||
return gitEmail;
|
||||
}
|
||||
|
||||
public List<String> getAlternateEmails() {
|
||||
return alternateEmails;
|
||||
}
|
||||
|
||||
public boolean isUsingEmail(String email) {
|
||||
return email.equals(this.email) || email.equals(gitEmail) || alternateEmails.contains(email);
|
||||
}
|
||||
|
||||
public NameAndEmail getNameAndEmail() {
|
||||
return new NameAndEmail(getDisplayName(), email);
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,9 +6,9 @@ import org.eclipse.jgit.revwalk.RevCommit;
|
||||
|
||||
import io.onedev.commons.loader.ExtensionPoint;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.support.administration.GroovyScript;
|
||||
|
||||
/**
|
||||
@ -26,9 +26,10 @@ public abstract class ScriptContribution {
|
||||
Build build = Build.get();
|
||||
if (build != null) {
|
||||
RevCommit commit = Build.get().getProject().getRevCommit(build.getCommitId(), true);
|
||||
User user = OneDev.getInstance(UserManager.class).find(commit.getCommitterIdent());
|
||||
if (user != null)
|
||||
return user.getName();
|
||||
EmailAddressManager emailAddressManager = OneDev.getInstance(EmailAddressManager.class);
|
||||
EmailAddress emailAddress = emailAddressManager.findByPersonIdent(commit.getCommitterIdent());
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
return emailAddress.getOwner().getName();
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
|
||||
@ -37,7 +37,8 @@ public class UserNameValidator implements ConstraintValidator<UserName, String>
|
||||
}
|
||||
constraintContext.buildConstraintViolationWithTemplate(message).addConstraintViolation();
|
||||
return false;
|
||||
} else if (value.equals("new") || value.equals(User.SYSTEM_NAME) || value.equals(User.UNKNOWN_NAME)) {
|
||||
} else if (value.equals("new") || value.equals(User.SYSTEM_NAME.toLowerCase())
|
||||
|| value.equals(User.UNKNOWN_NAME.toLowerCase())) {
|
||||
constraintContext.disableDefaultConstraintViolation();
|
||||
String message = this.message;
|
||||
if (message.length() == 0)
|
||||
@ -54,7 +55,7 @@ public class UserNameValidator implements ConstraintValidator<UserName, String>
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
String normalizedUserName = preferredUserName.replaceAll("[^\\w-\\.]", "-");
|
||||
String normalizedUserName = preferredUserName.replaceAll("[^\\w-\\.]", "-").toLowerCase();
|
||||
int suffix = 1;
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
while (true) {
|
||||
|
||||
@ -51,6 +51,7 @@ import io.onedev.server.web.page.admin.user.accesstoken.UserAccessTokenPage;
|
||||
import io.onedev.server.web.page.admin.user.authorization.UserAuthorizationsPage;
|
||||
import io.onedev.server.web.page.admin.user.avatar.UserAvatarPage;
|
||||
import io.onedev.server.web.page.admin.user.create.NewUserPage;
|
||||
import io.onedev.server.web.page.admin.user.emailaddresses.UserEmailAddressesPage;
|
||||
import io.onedev.server.web.page.admin.user.membership.UserMembershipsPage;
|
||||
import io.onedev.server.web.page.admin.user.password.UserPasswordPage;
|
||||
import io.onedev.server.web.page.admin.user.profile.UserProfilePage;
|
||||
@ -64,6 +65,7 @@ import io.onedev.server.web.page.help.ResourceListPage;
|
||||
import io.onedev.server.web.page.issues.IssueListPage;
|
||||
import io.onedev.server.web.page.my.accesstoken.MyAccessTokenPage;
|
||||
import io.onedev.server.web.page.my.avatar.MyAvatarPage;
|
||||
import io.onedev.server.web.page.my.emailaddresses.MyEmailAddressesPage;
|
||||
import io.onedev.server.web.page.my.password.MyPasswordPage;
|
||||
import io.onedev.server.web.page.my.profile.MyProfilePage;
|
||||
import io.onedev.server.web.page.my.sshkeys.MySshKeysPage;
|
||||
@ -125,6 +127,7 @@ import io.onedev.server.web.page.project.tags.ProjectTagsPage;
|
||||
import io.onedev.server.web.page.pullrequests.PullRequestListPage;
|
||||
import io.onedev.server.web.page.simple.error.MethodNotAllowedErrorPage;
|
||||
import io.onedev.server.web.page.simple.error.PageNotFoundErrorPage;
|
||||
import io.onedev.server.web.page.simple.security.EmailAddressVerificationPage;
|
||||
import io.onedev.server.web.page.simple.security.LoginPage;
|
||||
import io.onedev.server.web.page.simple.security.LogoutPage;
|
||||
import io.onedev.server.web.page.simple.security.PasswordResetPage;
|
||||
@ -174,6 +177,7 @@ public class BaseUrlMapper extends CompoundRequestMapper {
|
||||
|
||||
private void addMyPages() {
|
||||
add(new DynamicPathPageMapper("my/profile", MyProfilePage.class));
|
||||
add(new DynamicPathPageMapper("my/email-addresses", MyEmailAddressesPage.class));
|
||||
add(new DynamicPathPageMapper("my/avatar", MyAvatarPage.class));
|
||||
add(new DynamicPathPageMapper("my/password", MyPasswordPage.class));
|
||||
add(new DynamicPathPageMapper("my/ssh-keys", MySshKeysPage.class));
|
||||
@ -213,6 +217,8 @@ public class BaseUrlMapper extends CompoundRequestMapper {
|
||||
add(new DynamicPathPageMapper("logout", LogoutPage.class));
|
||||
add(new DynamicPathPageMapper("signup", SignUpPage.class));
|
||||
add(new DynamicPathPageMapper("reset-password", PasswordResetPage.class));
|
||||
add(new DynamicPathPageMapper("verify-email-address/${emailAddress}/${verificationCode}",
|
||||
EmailAddressVerificationPage.class));
|
||||
add(new DynamicPathPageMapper(SsoProcessPage.MOUNT_PATH + "/${stage}/${connector}", SsoProcessPage.class));
|
||||
}
|
||||
|
||||
@ -221,6 +227,7 @@ public class BaseUrlMapper extends CompoundRequestMapper {
|
||||
add(new DynamicPathPageMapper("administration/users", UserListPage.class));
|
||||
add(new DynamicPathPageMapper("administration/users/new", NewUserPage.class));
|
||||
add(new DynamicPathPageMapper("administration/users/${user}/profile", UserProfilePage.class));
|
||||
add(new DynamicPathPageMapper("administration/users/${user}/email-setting", UserEmailAddressesPage.class));
|
||||
add(new DynamicPathPageMapper("administration/users/${user}/groups", UserMembershipsPage.class));
|
||||
add(new DynamicPathPageMapper("administration/users/${user}/authorizations", UserAuthorizationsPage.class));
|
||||
add(new DynamicPathPageMapper("administration/users/${user}/avatar", UserAvatarPage.class));
|
||||
|
||||
@ -8,7 +8,6 @@ import org.eclipse.jgit.lib.PersonIdent;
|
||||
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
|
||||
public interface AvatarManager {
|
||||
|
||||
@ -22,7 +21,7 @@ public interface AvatarManager {
|
||||
|
||||
void useAvatar(Project project, @Nullable String avatarData);
|
||||
|
||||
File getUploaded(UserFacade user);
|
||||
File getUploaded(User user);
|
||||
|
||||
File getUploaded(Project project);
|
||||
|
||||
|
||||
@ -20,12 +20,12 @@ import io.onedev.commons.bootstrap.Bootstrap;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.commons.utils.LockUtils;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
import io.onedev.server.web.component.avatarupload.AvatarUploadField;
|
||||
|
||||
@Singleton
|
||||
@ -37,12 +37,12 @@ public class DefaultAvatarManager implements AvatarManager {
|
||||
|
||||
private final SettingManager settingManager;
|
||||
|
||||
private final UserManager userManager;
|
||||
private final EmailAddressManager emailAddressManager;
|
||||
|
||||
@Inject
|
||||
public DefaultAvatarManager(SettingManager settingManager, UserManager userManager) {
|
||||
public DefaultAvatarManager(SettingManager settingManager, EmailAddressManager emailAddressManager) {
|
||||
this.settingManager = settingManager;
|
||||
this.userManager = userManager;
|
||||
this.emailAddressManager = emailAddressManager;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@ -53,13 +53,17 @@ public class DefaultAvatarManager implements AvatarManager {
|
||||
} else if (user.isSystem()) {
|
||||
return AVATARS_BASE_URL + "onedev.png";
|
||||
} else {
|
||||
File uploadedFile = getUploaded(new UserFacade(user));
|
||||
File uploadedFile = getUploaded(user);
|
||||
if (uploadedFile.exists())
|
||||
return AVATARS_BASE_URL + "uploaded/users/" + user.getId() + ".jpg?version=" + uploadedFile.lastModified();
|
||||
if (settingManager.getSystemSetting().isGravatarEnabled())
|
||||
return Gravatar.getURL(user.getEmail(), GRAVATAR_SIZE);
|
||||
else
|
||||
return generateAvatar(user.getName(), user.getEmail());
|
||||
|
||||
EmailAddress emailAddress = user.getPrimaryEmailAddress();
|
||||
if (emailAddress == null || !emailAddress.isVerified())
|
||||
return generateAvatar(user.getName(), null);
|
||||
else if (settingManager.getSystemSetting().isGravatarEnabled())
|
||||
return Gravatar.getURL(emailAddress.getValue(), GRAVATAR_SIZE);
|
||||
else
|
||||
return generateAvatar(user.getName(), emailAddress.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,22 +76,13 @@ public class DefaultAvatarManager implements AvatarManager {
|
||||
else
|
||||
return AVATARS_BASE_URL + "user.png";
|
||||
} else {
|
||||
UserFacade user = userManager.findFacadeByEmail(personIdent.getEmailAddress());
|
||||
if (user != null) {
|
||||
File uploadedFile = getUploaded(user);
|
||||
if (uploadedFile.exists())
|
||||
return AVATARS_BASE_URL + "uploaded/users/" + user.getId() + ".jpg?version=" + uploadedFile.lastModified();
|
||||
|
||||
if (settingManager.getSystemSetting().isGravatarEnabled())
|
||||
return Gravatar.getURL(user.getEmail(), GRAVATAR_SIZE);
|
||||
else
|
||||
return generateAvatar(user.getDisplayName(), user.getEmail());
|
||||
} else {
|
||||
if (settingManager.getSystemSetting().isGravatarEnabled())
|
||||
return Gravatar.getURL(personIdent.getEmailAddress(), GRAVATAR_SIZE);
|
||||
else
|
||||
return generateAvatar(personIdent.getName(), personIdent.getEmailAddress());
|
||||
}
|
||||
EmailAddress emailAddress = emailAddressManager.findByValue(personIdent.getEmailAddress());
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
return getAvatarUrl(emailAddress.getOwner());
|
||||
else if (settingManager.getSystemSetting().isGravatarEnabled())
|
||||
return Gravatar.getURL(personIdent.getEmailAddress(), GRAVATAR_SIZE);
|
||||
else
|
||||
return generateAvatar(personIdent.getName(), personIdent.getEmailAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +129,7 @@ public class DefaultAvatarManager implements AvatarManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getUploaded(UserFacade user) {
|
||||
public File getUploaded(User user) {
|
||||
return new File(Bootstrap.getSiteDir(), "avatars/uploaded/users/" + user.getId() + ".jpg");
|
||||
}
|
||||
|
||||
@ -144,7 +139,7 @@ public class DefaultAvatarManager implements AvatarManager {
|
||||
Lock avatarLock = LockUtils.getLock("uploaded-user-avatar:" + user.getId());
|
||||
avatarLock.lock();
|
||||
try {
|
||||
File avatarFile = getUploaded(new UserFacade(user));
|
||||
File avatarFile = getUploaded(user);
|
||||
FileUtils.createDir(avatarFile.getParentFile());
|
||||
AvatarUploadField.writeToFile(avatarFile, avatarData);
|
||||
} finally {
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
package io.onedev.server.web.component;
|
||||
|
||||
import org.apache.wicket.behavior.AttributeAppender;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class EmailAddressVerificationStatusBadge extends Label {
|
||||
|
||||
private final IModel<EmailAddress> emailAddressModel;
|
||||
|
||||
public EmailAddressVerificationStatusBadge(String id, IModel<EmailAddress> emailAddressModel) {
|
||||
super(id);
|
||||
this.emailAddressModel = emailAddressModel;
|
||||
setDefaultModel(new LoadableDetachableModel<String>() {
|
||||
|
||||
@Override
|
||||
protected String load() {
|
||||
return !emailAddressModel.getObject().isVerified()?"Unverified":"";
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach() {
|
||||
emailAddressModel.detach();
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
setVisible(!emailAddressModel.getObject().isVerified());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
add(AttributeAppender.append("class", new LoadableDetachableModel<String>() {
|
||||
|
||||
@Override
|
||||
protected String load() {
|
||||
if (!emailAddressModel.getObject().isVerified())
|
||||
return "badge badge-sm badge-warning";
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@ -807,8 +807,7 @@ public abstract class RevisionDiffPanel extends Panel {
|
||||
for (User user: getProject().getAuthors(mark.getPath(),
|
||||
ObjectId.fromString(mark.getCommitHash()),
|
||||
new LinearRange(commentRange.getFromRow(), commentRange.getToRow()))) {
|
||||
if (user.getEmail() != null)
|
||||
mentions.append("@").append(user.getName()).append(" ");
|
||||
mentions.append("@").append(user.getName()).append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -221,8 +221,7 @@ public class MarkdownViewer extends GenericPanel<String> {
|
||||
String avatarUrl = OneDev.getInstance(AvatarManager.class).getAvatarUrl(user);
|
||||
String script = String.format("onedev.server.markdown.renderUserTooltip('%s', '%s', '%s')",
|
||||
JavaScriptEscape.escapeJavaScript(avatarUrl),
|
||||
JavaScriptEscape.escapeJavaScript(user.getDisplayName()),
|
||||
JavaScriptEscape.escapeJavaScript(user.getEmail()));
|
||||
JavaScriptEscape.escapeJavaScript(user.getDisplayName()));
|
||||
target.appendJavaScript(script);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -996,7 +996,7 @@ onedev.server.markdown = {
|
||||
$tooltip.find(".title").text(title);
|
||||
$tooltip.align({placement: $tooltip.data("alignment"), target: {element: $tooltip.data("trigger")}});
|
||||
},
|
||||
renderUserTooltip: function(avatarUrl, name, email) {
|
||||
renderUserTooltip: function(avatarUrl, name) {
|
||||
var $tooltip = $("#reference-tooltip");
|
||||
$tooltip.empty().append("" +
|
||||
"<div class='d-flex align-items-center'>" +
|
||||
|
||||
@ -21,7 +21,7 @@ import io.onedev.server.model.Project;
|
||||
import io.onedev.server.search.entity.project.ProjectQuery;
|
||||
import io.onedev.server.search.entity.project.ProjectQueryLexer;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.EmailAddress;
|
||||
import io.onedev.server.util.ParsedEmailAddress;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
import io.onedev.server.web.component.markdown.MarkdownViewer;
|
||||
import io.onedev.server.web.component.modal.ModalLink;
|
||||
@ -101,7 +101,7 @@ public abstract class ProjectInfoPanel extends Panel {
|
||||
|
||||
String subAddressed;
|
||||
|
||||
EmailAddress emailAddress = EmailAddress.parse(settingManager.getMailSetting().getEmailAddress());
|
||||
ParsedEmailAddress emailAddress = ParsedEmailAddress.parse(settingManager.getMailSetting().getEmailAddress());
|
||||
if (getProject().getServiceDeskName() != null)
|
||||
subAddressed = emailAddress.getSubAddressed(getProject().getServiceDeskName());
|
||||
else
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
display: none;
|
||||
}
|
||||
.project-list .row-selector {
|
||||
padding-top: 1rem;
|
||||
padding-top: 16px;
|
||||
}
|
||||
.project-list .project-summary {
|
||||
margin-left: 2px;
|
||||
|
||||
@ -191,7 +191,7 @@
|
||||
*/
|
||||
.select2-container .select2-choice .select2-arrow b,
|
||||
.select2-container .select2-choice div b {
|
||||
background-position: 0 6px;
|
||||
background-position: 0 8px;
|
||||
}
|
||||
|
||||
.select2-dropdown-open .select2-choice .select2-arrow b,
|
||||
@ -203,28 +203,28 @@
|
||||
.input-group-sm .select2-container .select2-choice .select2-arrow b,
|
||||
.select2-container.form-control-sm .select2-choice div b,
|
||||
.input-group-sm .select2-container .select2-choice div b {
|
||||
background-position: 0 1px;
|
||||
background-position: 0 2px;
|
||||
}
|
||||
|
||||
.select2-dropdown-open.form-control-sm .select2-choice .select2-arrow b,
|
||||
.input-group-sm .select2-dropdown-open .select2-choice .select2-arrow b,
|
||||
.select2-dropdown-open.form-control-sm .select2-choice div b,
|
||||
.input-group-sm .select2-dropdown-open .select2-choice div b {
|
||||
background-position: -18px 1px;
|
||||
background-position: -18px 2px;
|
||||
}
|
||||
|
||||
.select2-container.form-control-lg .select2-choice .select2-arrow b,
|
||||
.input-group-lg .select2-container .select2-choice .select2-arrow b,
|
||||
.select2-container.form-control-lg .select2-choice div b,
|
||||
.input-group-lg .select2-container .select2-choice div b {
|
||||
background-position: 0 9px;
|
||||
background-position: 0 10px;
|
||||
}
|
||||
|
||||
.select2-dropdown-open.form-control-lg .select2-choice .select2-arrow b,
|
||||
.input-group-lg .select2-dropdown-open .select2-choice .select2-arrow b,
|
||||
.select2-dropdown-open.form-control-lg .select2-choice div b,
|
||||
.input-group-lg .select2-dropdown-open .select2-choice div b {
|
||||
background-position: -18px 9px;
|
||||
background-position: -18px 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -12,7 +12,6 @@ import org.apache.wicket.model.PropertyModel;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
import io.onedev.server.web.avatar.AvatarManager;
|
||||
import io.onedev.server.web.component.avatarupload.AvatarFileSelected;
|
||||
import io.onedev.server.web.component.avatarupload.AvatarUploadField;
|
||||
@ -46,7 +45,7 @@ public class AvatarEditPanel extends GenericPanel<User> {
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
setVisible(getAvatarManager().getUploaded(new UserFacade(getUser())).exists());
|
||||
setVisible(getAvatarManager().getUploaded(getUser()).exists());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -10,9 +10,9 @@ import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
import io.onedev.server.web.component.user.UserAvatar;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@ -39,12 +39,14 @@ public class PersonCardPanel extends Panel {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
EmailAddressManager emailAddressManager = OneDev.getInstance(EmailAddressManager.class);
|
||||
String displayName;
|
||||
UserFacade user = OneDev.getInstance(UserManager.class).findFacadeByEmail(personIdent.getEmailAddress());
|
||||
if (user != null)
|
||||
displayName = user.getDisplayName();
|
||||
EmailAddress emailAddress = emailAddressManager.findByValue(personIdent.getEmailAddress());
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
displayName = emailAddress.getOwner().getDisplayName();
|
||||
else
|
||||
displayName = personIdent.getName();
|
||||
|
||||
builder.append("<div>" + HtmlEscape.escapeHtml5(displayName) + " <i>(" + gitRole + ")</i></div>");
|
||||
|
||||
if (StringUtils.isBlank(personIdent.getEmailAddress())) {
|
||||
@ -53,8 +55,8 @@ public class PersonCardPanel extends Panel {
|
||||
else
|
||||
builder.append("<i>No OneDev Account</i>");
|
||||
} else {
|
||||
if (user != null)
|
||||
builder.append("<i>@" + HtmlEscape.escapeHtml5(user.getName()) + "</i>");
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
builder.append("<i>@" + HtmlEscape.escapeHtml5(emailAddress.getOwner().getName()) + "</i>");
|
||||
else
|
||||
builder.append("<i>No OneDev Account</i>");
|
||||
}
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
<wicket:panel>
|
||||
<div class="email-addresses">
|
||||
<ul class="alert alert-light px-5 mb-5">
|
||||
<li>Primary email address will be used to receive notifications, show gravatar (if enabled) etc.</li>
|
||||
<li>Git email address will be used as git author/committer for commits created on web UI</li>
|
||||
<li>When determine if <span wicket:id="who"></span> author/committer of a git commit, all emails listed here will be checked</li>
|
||||
<li>Unverified email address is <b>NOT</b> applicable for above functionalities</li>
|
||||
</ul>
|
||||
<table class="table">
|
||||
<tr wicket:id="emailAddresses">
|
||||
<td>
|
||||
<span wicket:id="value" class="font-size-lg"></span>
|
||||
<span wicket:id="primary" class="badge badge-info badge-sm ml-3">Primary</span>
|
||||
<span wicket:id="git" class="badge badge-info badge-sm ml-1">Git</span>
|
||||
<span wicket:id="verificationStatus" class="ml-1"></span>
|
||||
<div wicket:id="externalManagedNote" class="text-warning font-size-sm mt-2"></div>
|
||||
</td>
|
||||
<td>
|
||||
<a wicket:id="operations" title="Operatioins"><wicket:svg href="ellipsis" class="icon"/></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="mb-2 mt-4 font-weight-bolder">Add New Email Address</div>
|
||||
<div class="form-group">
|
||||
<form wicket:id="form" class="form-inline">
|
||||
<input wicket:id="emailAddress" class="form-control form-control-sm flex-flow-0 mr-3" placeholder="Email Address">
|
||||
<button type="submit" class="btn btn-icon btn-sm btn-primary"><wicket:svg href="plus" class="icon"/></button>
|
||||
</form>
|
||||
<div wicket:id="feedback"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</wicket:panel>
|
||||
@ -0,0 +1,254 @@
|
||||
package io.onedev.server.web.component.user.emailaddresses;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.wicket.Session;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.markup.html.AjaxLink;
|
||||
import org.apache.wicket.feedback.FencedFeedbackPanel;
|
||||
import org.apache.wicket.markup.html.WebMarkupContainer;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.markup.html.form.TextField;
|
||||
import org.apache.wicket.markup.html.link.Link;
|
||||
import org.apache.wicket.markup.html.list.ListItem;
|
||||
import org.apache.wicket.markup.html.list.ListView;
|
||||
import org.apache.wicket.markup.html.panel.GenericPanel;
|
||||
import org.apache.wicket.model.AbstractReadOnlyModel;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.validation.validator.EmailAddressValidator;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.web.component.EmailAddressVerificationStatusBadge;
|
||||
import io.onedev.server.web.component.floating.FloatingPanel;
|
||||
import io.onedev.server.web.component.menu.MenuItem;
|
||||
import io.onedev.server.web.component.menu.MenuLink;
|
||||
import io.onedev.server.web.page.my.MyPage;
|
||||
import io.onedev.server.web.util.ConfirmClickModifier;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class EmailAddressesPanel extends GenericPanel<User> {
|
||||
|
||||
private String emailAddressValue;
|
||||
|
||||
public EmailAddressesPanel(String id, IModel<User> model) {
|
||||
super(id, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
if (getPage() instanceof MyPage)
|
||||
add(new Label("who", "you are "));
|
||||
else
|
||||
add(new Label("who", "this user is "));
|
||||
|
||||
add(new ListView<EmailAddress>("emailAddresses", new AbstractReadOnlyModel<List<EmailAddress>>() {
|
||||
|
||||
@Override
|
||||
public List<EmailAddress> getObject() {
|
||||
return getUser().getSortedEmailAddresses();
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
@Override
|
||||
protected void populateItem(ListItem<EmailAddress> item) {
|
||||
EmailAddress address = item.getModelObject();
|
||||
item.add(new Label("value", address.getValue()));
|
||||
|
||||
item.add(new WebMarkupContainer("primary")
|
||||
.setVisible(address.equals(getUser().getPrimaryEmailAddress())));
|
||||
item.add(new WebMarkupContainer("git")
|
||||
.setVisible(address.equals(getUser().getGitEmailAddress())));
|
||||
|
||||
item.add(new EmailAddressVerificationStatusBadge("verificationStatus", item.getModel()));
|
||||
|
||||
if (getUser().isExternalManaged() && address.equals(getUser().getPrimaryEmailAddress())) {
|
||||
item.add(new Label("externalManagedNote",
|
||||
"This primary email address is managed from " + getUser().getAuthSource()));
|
||||
} else {
|
||||
item.add(new WebMarkupContainer("externalManagedNote").setVisible(false));
|
||||
}
|
||||
|
||||
item.add(new MenuLink("operations") {
|
||||
|
||||
@Override
|
||||
protected List<MenuItem> getMenuItems(FloatingPanel dropdown) {
|
||||
List<MenuItem> menuItems = new ArrayList<>();
|
||||
EmailAddress address = item.getModelObject();
|
||||
Long addressId = address.getId();
|
||||
if (!getUser().isExternalManaged() && !address.equals(getUser().getPrimaryEmailAddress())) {
|
||||
menuItems.add(new MenuItem() {
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return "Set As Primary";
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebMarkupContainer newLink(String id) {
|
||||
return new Link<Void>(id) {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
getEmailAddressManager().setAsPrimary(getEmailAddressManager().load(addressId));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
if (!address.equals(getUser().getGitEmailAddress())) {
|
||||
menuItems.add(new MenuItem() {
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return "Use For Git Operations";
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebMarkupContainer newLink(String id) {
|
||||
return new Link<Void>(id) {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
getEmailAddressManager().useForGitOperations(getEmailAddressManager().load(addressId));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
if (!address.isVerified()) {
|
||||
menuItems.add(new MenuItem() {
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return "Resend Verification Email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebMarkupContainer newLink(String id) {
|
||||
return new AjaxLink<Void>(id) {
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
if (OneDev.getInstance(SettingManager.class).getMailSetting() != null) {
|
||||
getEmailAddressManager().sendVerificationEmail(item.getModelObject());
|
||||
Session.get().success("Verification email sent, please check it");
|
||||
} else {
|
||||
target.appendJavaScript(String.format("alert('%s');",
|
||||
"Unable to send verification email as system mail setting is not defined yet"));
|
||||
}
|
||||
dropdown.close();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
if (!(getUser().isExternalManaged() && address.equals(getUser().getPrimaryEmailAddress()))
|
||||
&& getUser().getEmailAddresses().size() > 1) {
|
||||
menuItems.add(new MenuItem() {
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return "Delete";
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebMarkupContainer newLink(String id) {
|
||||
Link<Void> link = new Link<Void>(id) {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
getEmailAddressManager().delete(getEmailAddressManager().load(addressId));
|
||||
}
|
||||
|
||||
};
|
||||
link.add(new ConfirmClickModifier("Do you really want to delete this email address?"));
|
||||
return link;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
setVisible(!getMenuItems(null).isEmpty());
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Form<?> form = new Form<Void>("form") {
|
||||
|
||||
@Override
|
||||
protected void onSubmit() {
|
||||
super.onSubmit();
|
||||
|
||||
if (getEmailAddressManager().findByValue(emailAddressValue) != null) {
|
||||
error("This email address is being used");
|
||||
} else {
|
||||
EmailAddress address = new EmailAddress();
|
||||
address.setValue(emailAddressValue);
|
||||
address.setOwner(getUser());
|
||||
if (SecurityUtils.isAdministrator())
|
||||
address.setVerificationCode(null);
|
||||
getEmailAddressManager().save(address);
|
||||
emailAddressValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
TextField<String> input = new TextField<String>("emailAddress", new IModel<String>() {
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getObject() {
|
||||
return emailAddressValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(String object) {
|
||||
emailAddressValue = object;
|
||||
}
|
||||
|
||||
});
|
||||
input.setLabel(Model.of("Email address"));
|
||||
input.setRequired(true);
|
||||
input.add(EmailAddressValidator.getInstance());
|
||||
form.add(input);
|
||||
add(new FencedFeedbackPanel("feedback", form));
|
||||
add(form);
|
||||
}
|
||||
|
||||
private EmailAddressManager getEmailAddressManager() {
|
||||
return OneDev.getInstance(EmailAddressManager.class);
|
||||
}
|
||||
|
||||
private User getUser() {
|
||||
return getModelObject();
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,8 +10,8 @@ import org.apache.wicket.markup.html.panel.Panel;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.web.behavior.dropdown.DropdownHoverBehavior;
|
||||
import io.onedev.server.web.component.floating.AlignPlacement;
|
||||
import io.onedev.server.web.component.user.UserAvatar;
|
||||
@ -39,9 +39,10 @@ public class PersonIdentPanel extends Panel {
|
||||
|
||||
add(new UserAvatar("avatar", personIdent).setVisible(mode != Mode.NAME));
|
||||
|
||||
UserFacade user = OneDev.getInstance(UserManager.class).findFacadeByEmail(personIdent.getEmailAddress());
|
||||
if (user != null)
|
||||
add(new Label("name", user.getDisplayName()).setVisible(mode != Mode.AVATAR));
|
||||
EmailAddressManager emailAddressManager = OneDev.getInstance(EmailAddressManager.class);
|
||||
EmailAddress emailAddress = emailAddressManager.findByValue(personIdent.getEmailAddress());
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
add(new Label("name", emailAddress.getOwner().getDisplayName()).setVisible(mode != Mode.AVATAR));
|
||||
else
|
||||
add(new Label("name", personIdent.getName()).setVisible(mode != Mode.AVATAR));
|
||||
|
||||
|
||||
@ -65,9 +65,6 @@ public class ProfileEditPanel extends GenericPanel<User> {
|
||||
super.onSubmit();
|
||||
|
||||
User user = getUser();
|
||||
user.getAlternateEmails().remove(user.getEmail());
|
||||
if (user.getGitEmail() != null)
|
||||
user.getAlternateEmails().remove(user.getGitEmail());
|
||||
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
User userWithSameName = userManager.findByName(user.getName());
|
||||
@ -75,25 +72,6 @@ public class ProfileEditPanel extends GenericPanel<User> {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_NAME)),
|
||||
"Login name already used by another account");
|
||||
}
|
||||
User userWithSameEmail = userManager.findByEmail(user.getEmail());
|
||||
if (userWithSameEmail != null && !userWithSameEmail.equals(user)) {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_EMAIL)),
|
||||
"Email already used by another account");
|
||||
}
|
||||
if (user.getGitEmail() != null) {
|
||||
userWithSameEmail = userManager.findByEmail(user.getGitEmail());
|
||||
if (userWithSameEmail != null && !userWithSameEmail.equals(user)) {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_GIT_EMAIL)),
|
||||
"Email already used by another account");
|
||||
}
|
||||
}
|
||||
for (String email: user.getAlternateEmails()) {
|
||||
userWithSameEmail = userManager.findByEmail(email);
|
||||
if (userWithSameEmail != null && !userWithSameEmail.equals(user)) {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_ALTERNATE_EMAILS)),
|
||||
"Email '" + email + "' already used by another account");
|
||||
}
|
||||
}
|
||||
|
||||
if (editor.isValid()) {
|
||||
userManager.save(user, oldName);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.twofactorauthentication;
|
||||
package io.onedev.server.web.component.user.twofactorauthentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
@ -23,6 +23,9 @@ import org.apache.wicket.model.AbstractReadOnlyModel;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class Wizard extends Panel {
|
||||
|
||||
@ -116,7 +119,14 @@ public abstract class Wizard extends Panel {
|
||||
@Override
|
||||
public void onSubmit() {
|
||||
super.onSubmit();
|
||||
getActiveStep().complete();
|
||||
OneDev.getInstance(TransactionManager.class).run(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
getActiveStep().complete();
|
||||
}
|
||||
|
||||
});
|
||||
activeStepIndex++;
|
||||
form.replace(getActiveStep().render("content"));
|
||||
}
|
||||
@ -133,7 +143,14 @@ public abstract class Wizard extends Panel {
|
||||
@Override
|
||||
public void onSubmit() {
|
||||
super.onSubmit();
|
||||
getActiveStep().complete();
|
||||
OneDev.getInstance(TransactionManager.class).run(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
getActiveStep().complete();
|
||||
}
|
||||
|
||||
});
|
||||
finished();
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import java.util.stream.Collectors;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.util.convert.ConversionException;
|
||||
|
||||
@ -50,7 +51,7 @@ public class UserMultiChoiceEditor extends PropertyEditor<List<String>> {
|
||||
choices.addAll((List<User>)ReflectionUtils
|
||||
.invokeStaticMethod(descriptor.getBeanClass(), userChoice.value()));
|
||||
} else {
|
||||
choices.addAll(OneDev.getInstance(UserManager.class).query());
|
||||
choices.addAll(getUserManager().query());
|
||||
choices.sort(Comparator.comparing(User::getDisplayName));
|
||||
}
|
||||
} finally {
|
||||
@ -59,15 +60,42 @@ public class UserMultiChoiceEditor extends PropertyEditor<List<String>> {
|
||||
|
||||
List<User> selections = new ArrayList<>();
|
||||
if (getModelObject() != null) {
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
for (String userName: getModelObject()) {
|
||||
User user = userManager.findByName(userName);
|
||||
User user = getUserManager().findByName(userName);
|
||||
if (user != null && choices.contains(user))
|
||||
selections.add(user);
|
||||
}
|
||||
}
|
||||
|
||||
input = new UserMultiChoice("input", Model.of(selections), Model.of(choices)) {
|
||||
List<Long> choiceIds = choices.stream().map(it->it.getId()).collect(Collectors.toList());
|
||||
List<Long> selectionIds = selections.stream().map(it->it.getId()).collect(Collectors.toList());
|
||||
|
||||
input = new UserMultiChoice("input", new IModel<Collection<User>>() {
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<User> getObject() {
|
||||
return selectionIds.stream().map(it-> getUserManager().load(it)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(Collection<User> object) {
|
||||
selectionIds.clear();
|
||||
if (object != null)
|
||||
selectionIds.addAll(object.stream().map(it->it.getId()).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
}, new LoadableDetachableModel<Collection<User>>() {
|
||||
|
||||
@Override
|
||||
protected Collection<User> load() {
|
||||
return choiceIds.stream().map(it-> getUserManager().load(it)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
@ -90,6 +118,10 @@ public class UserMultiChoiceEditor extends PropertyEditor<List<String>> {
|
||||
|
||||
add(input);
|
||||
}
|
||||
|
||||
private UserManager getUserManager() {
|
||||
return OneDev.getInstance(UserManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> convertInputToValue() throws ConversionException {
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
package io.onedev.server.web.editable.userchoice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.util.convert.ConversionException;
|
||||
|
||||
@ -32,6 +36,10 @@ public class UserSingleChoiceEditor extends PropertyEditor<String> {
|
||||
super(id, propertyDescriptor, propertyModel);
|
||||
}
|
||||
|
||||
private UserManager getUserManager() {
|
||||
return OneDev.getInstance(UserManager.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
@ -48,7 +56,7 @@ public class UserSingleChoiceEditor extends PropertyEditor<String> {
|
||||
choices.addAll((List<User>)ReflectionUtils
|
||||
.invokeStaticMethod(descriptor.getBeanClass(), userChoice.value()));
|
||||
} else {
|
||||
choices.addAll(OneDev.getInstance(UserManager.class).query());
|
||||
choices.addAll(getUserManager().query());
|
||||
choices.sort(Comparator.comparing(User::getDisplayName));
|
||||
}
|
||||
} finally {
|
||||
@ -57,14 +65,43 @@ public class UserSingleChoiceEditor extends PropertyEditor<String> {
|
||||
|
||||
User selection;
|
||||
if (getModelObject() != null)
|
||||
selection = OneDev.getInstance(UserManager.class).findByName(getModelObject());
|
||||
selection = getUserManager().findByName(getModelObject());
|
||||
else
|
||||
selection = null;
|
||||
|
||||
if (selection != null && !choices.contains(selection))
|
||||
selection = null;
|
||||
|
||||
input = new UserSingleChoice("input", Model.of(selection), Model.of(choices)) {
|
||||
List<Long> choiceIds = choices.stream().map(it->it.getId()).collect(Collectors.toList());
|
||||
|
||||
AtomicReference<Long> selectionId = new AtomicReference<>(null);
|
||||
if (selection != null)
|
||||
selectionId.set(selection.getId());
|
||||
|
||||
input = new UserSingleChoice("input", new IModel<User>() {
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getObject() {
|
||||
return selectionId.get()!=null? getUserManager().load(selectionId.get()): null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(User object) {
|
||||
selectionId.set(User.idOf(object));
|
||||
}
|
||||
|
||||
}, new LoadableDetachableModel<Collection<User>>() {
|
||||
|
||||
@Override
|
||||
protected Collection<User> load() {
|
||||
return choiceIds.stream().map(it-> getUserManager().load(it)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<input wicket:id="addNew" type="hidden" class="form-control">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<a wicket:id="delete" class="btn btn-light-danger">Delete</a>
|
||||
<a wicket:id="delete" class="btn btn-light-danger">Delete <wicket:svg href="arrow" class="rotate-90 icon"/></a>
|
||||
</div>
|
||||
</div>
|
||||
<table wicket:id="memberships" class="memberships table"></table>
|
||||
@ -16,4 +16,8 @@
|
||||
<wicket:fragment wicket:id="nameFrag">
|
||||
<a wicket:id="link"><img wicket:id="avatar"> <span wicket:id="name" class="name"></span></a>
|
||||
</wicket:fragment>
|
||||
<wicket:fragment wicket:id="emailFrag">
|
||||
<span wicket:id="emailAddress"></span>
|
||||
<span wicket:id="verificationStatus" class="ml-1"></span>
|
||||
</wicket:fragment>
|
||||
</wicket:extend>
|
||||
@ -36,6 +36,7 @@ import org.hibernate.criterion.Restrictions;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.MembershipManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Membership;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
@ -44,6 +45,7 @@ import io.onedev.server.util.match.MatchScoreProvider;
|
||||
import io.onedev.server.util.match.MatchScoreUtils;
|
||||
import io.onedev.server.web.WebConstants;
|
||||
import io.onedev.server.web.behavior.OnTypingDoneBehavior;
|
||||
import io.onedev.server.web.component.EmailAddressVerificationStatusBadge;
|
||||
import io.onedev.server.web.component.datatable.OneDataTable;
|
||||
import io.onedev.server.web.component.datatable.selectioncolumn.SelectionColumn;
|
||||
import io.onedev.server.web.component.floating.FloatingPanel;
|
||||
@ -324,13 +326,23 @@ public class GroupMembershipsPage extends GroupPage {
|
||||
}
|
||||
});
|
||||
|
||||
columns.add(new AbstractColumn<Membership, Void>(Model.of("Email")) {
|
||||
columns.add(new AbstractColumn<Membership, Void>(Model.of("Primary Email")) {
|
||||
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<Membership>> cellItem, String componentId,
|
||||
IModel<Membership> rowModel) {
|
||||
cellItem.add(new Label(componentId, rowModel.getObject().getUser().getEmail()));
|
||||
EmailAddress emailAddress = rowModel.getObject().getUser().getPrimaryEmailAddress();
|
||||
if (emailAddress != null) {
|
||||
Fragment fragment = new Fragment(componentId, "emailFrag", GroupMembershipsPage.this);
|
||||
fragment.add(new Label("emailAddress", emailAddress.getValue()));
|
||||
fragment.add(new EmailAddressVerificationStatusBadge(
|
||||
"verificationStatus", Model.of(emailAddress)));
|
||||
cellItem.add(fragment);
|
||||
} else {
|
||||
cellItem.add(new Label(componentId, "<i>Not specified</i>").setEscapeModelStrings(false));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
dataProvider = new SortableDataProvider<Membership, Void>() {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package io.onedev.server.web.page.admin.mailsetting;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
@ -22,17 +23,18 @@ import org.apache.wicket.util.visit.IVisitor;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.administration.MailSetting;
|
||||
import io.onedev.server.model.support.administration.ReceiveMailSetting;
|
||||
import io.onedev.server.notification.MailManager;
|
||||
import io.onedev.server.notification.MailPosition;
|
||||
import io.onedev.server.notification.MessageListener;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.EmailAddress;
|
||||
import io.onedev.server.util.ParsedEmailAddress;
|
||||
import io.onedev.server.web.component.taskbutton.TaskButton;
|
||||
import io.onedev.server.web.editable.BeanContext;
|
||||
import io.onedev.server.web.editable.BeanEditor;
|
||||
@ -66,6 +68,7 @@ public class MailSettingPage extends AdministrationPage {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
TaskButton testButton = new TaskButton("test") {
|
||||
|
||||
@Override
|
||||
@ -103,54 +106,64 @@ public class MailSettingPage extends AdministrationPage {
|
||||
|
||||
@Override
|
||||
protected String runTask(TaskLogger logger) {
|
||||
User user = SecurityUtils.getUser();
|
||||
|
||||
MailManager mailManager = OneDev.getInstance(MailManager.class);
|
||||
MailSetting mailSetting = mailSettingHolder.getMailSetting();
|
||||
if (mailSetting.getReceiveMailSetting() != null) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
AtomicReference<Future<?>> futureRef = new AtomicReference<>(null);
|
||||
MessageListener listener = new MessageListener() {
|
||||
|
||||
@Override
|
||||
public void onReceived(Message message) throws MessagingException {
|
||||
if (message.getSubject().contains(uuid))
|
||||
return OneDev.getInstance(SessionManager.class).call(new Callable<String>() {
|
||||
|
||||
@Override
|
||||
public String call() {
|
||||
MailManager mailManager = OneDev.getInstance(MailManager.class);
|
||||
MailSetting mailSetting = mailSettingHolder.getMailSetting();
|
||||
if (mailSetting.getReceiveMailSetting() != null) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
AtomicReference<Future<?>> futureRef = new AtomicReference<>(null);
|
||||
MessageListener listener = new MessageListener() {
|
||||
|
||||
@Override
|
||||
public void onReceived(Message message) throws MessagingException {
|
||||
if (message.getSubject().contains(uuid))
|
||||
futureRef.get().cancel(true);
|
||||
}
|
||||
|
||||
};
|
||||
futureRef.set(mailManager.monitorInbox(mailSetting.getReceiveMailSetting(),
|
||||
mailSetting.getTimeout(), listener, new MailPosition()));
|
||||
|
||||
ParsedEmailAddress emailAddress = ParsedEmailAddress.parse(mailSetting.getEmailAddress());
|
||||
String subAddressed = emailAddress.getSubAddressed(MailManager.TEST_SUB_ADDRESS);
|
||||
logger.log("Sending test mail to " + subAddressed + "...");
|
||||
mailManager.sendMail(mailSetting,
|
||||
Sets.newHashSet(subAddressed), Lists.newArrayList(), Lists.newArrayList(), uuid,
|
||||
"[Test] Test Email From OneDev", "This is a test email from OneDev", null, null);
|
||||
|
||||
logger.log("Waiting for test mail to come back...");
|
||||
|
||||
try {
|
||||
futureRef.get().get();
|
||||
} catch (CancellationException e) {
|
||||
} catch (InterruptedException e) {
|
||||
futureRef.get().cancel(true);
|
||||
}
|
||||
|
||||
};
|
||||
futureRef.set(mailManager.monitorInbox(mailSetting.getReceiveMailSetting(),
|
||||
mailSetting.getTimeout(), listener, new MailPosition()));
|
||||
|
||||
EmailAddress emailAddress = EmailAddress.parse(mailSetting.getEmailAddress());
|
||||
String subAddressed = emailAddress.getSubAddressed(MailManager.TEST_SUB_ADDRESS);
|
||||
logger.log("Sending test mail to " + subAddressed + "...");
|
||||
mailManager.sendMail(mailSetting,
|
||||
Sets.newHashSet(subAddressed), Lists.newArrayList(), Lists.newArrayList(), uuid,
|
||||
"[Test] Test Email From OneDev", "This is a test email from OneDev", null, null);
|
||||
|
||||
logger.log("Waiting for test mail to come back...");
|
||||
|
||||
try {
|
||||
futureRef.get().get();
|
||||
} catch (CancellationException e) {
|
||||
} catch (InterruptedException e) {
|
||||
futureRef.get().cancel(true);
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
logger.log("Received test mail");
|
||||
|
||||
return "Great, your mail setting is working";
|
||||
} else {
|
||||
io.onedev.server.model.EmailAddress emailAddress = SecurityUtils.getUser().getPrimaryEmailAddress();
|
||||
if (emailAddress != null) {
|
||||
String body = "Great, your mail setting is working!";
|
||||
mailManager.sendMail(mailSettingHolder.getMailSetting(), Sets.newHashSet(emailAddress.getValue()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), "[Test] Test Email From OneDev",
|
||||
body, body, null, null);
|
||||
return "Test mail has been sent to " + emailAddress.getValue() + ", please check your mail box";
|
||||
} else {
|
||||
throw new ExplicitException("Primary email address of your account is not specified yet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.log("Received test mail");
|
||||
|
||||
return "Great, your mail setting is working";
|
||||
} else {
|
||||
String body = "Great, your mail setting is working!";
|
||||
mailManager.sendMail(mailSettingHolder.getMailSetting(), Sets.newHashSet(user.getEmail()),
|
||||
Lists.newArrayList(), Lists.newArrayList(), "[Test] Test Email From OneDev",
|
||||
body, body, null, null);
|
||||
return "Test mail has been sent to " + user.getEmail() + ", please check your mail box";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@ -17,4 +17,8 @@
|
||||
<a wicket:id="impersonate" class="btn btn-xs btn-icon btn-light btn-hover-primary mr-1" title="Impersonate this user"><wicket:svg href="user-tick" class="icon align-middle"/></a>
|
||||
<a wicket:id="delete" class="btn btn-xs btn-icon btn-light btn-hover-danger" title="Delete this user"><wicket:svg href="trash" class="icon align-middle"></wicket:svg></a>
|
||||
</wicket:fragment>
|
||||
<wicket:fragment wicket:id="emailFrag">
|
||||
<span wicket:id="emailAddress"></span>
|
||||
<span wicket:id="verificationStatus" class="ml-1"></span>
|
||||
</wicket:fragment>
|
||||
</wicket:extend>
|
||||
|
||||
@ -29,19 +29,17 @@ import org.apache.wicket.model.LoadableDetachableModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.request.cycle.RequestCycle;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
import org.hibernate.criterion.MatchMode;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.web.WebConstants;
|
||||
import io.onedev.server.web.WebSession;
|
||||
import io.onedev.server.web.ajaxlistener.ConfirmClickListener;
|
||||
import io.onedev.server.web.behavior.OnTypingDoneBehavior;
|
||||
import io.onedev.server.web.component.EmailAddressVerificationStatusBadge;
|
||||
import io.onedev.server.web.component.datatable.OneDataTable;
|
||||
import io.onedev.server.web.component.link.ActionablePageLink;
|
||||
import io.onedev.server.web.component.user.UserAvatar;
|
||||
@ -73,19 +71,6 @@ public class UserListPage extends AdministrationPage {
|
||||
query = params.get(PARAM_QUERY).toString();
|
||||
}
|
||||
|
||||
private EntityCriteria<User> getCriteria() {
|
||||
EntityCriteria<User> criteria = EntityCriteria.of(User.class);
|
||||
criteria.add(Restrictions.gt("id", 0L));
|
||||
if (query != null) {
|
||||
criteria.add(Restrictions.or(
|
||||
Restrictions.ilike("name", query, MatchMode.ANYWHERE),
|
||||
Restrictions.ilike("fullName", query, MatchMode.ANYWHERE)));
|
||||
} else {
|
||||
criteria.setCacheable(true);
|
||||
}
|
||||
return criteria;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPopState(AjaxRequestTarget target, Serializable data) {
|
||||
super.onPopState(target, data);
|
||||
@ -203,7 +188,7 @@ public class UserListPage extends AdministrationPage {
|
||||
|
||||
});
|
||||
|
||||
columns.add(new AbstractColumn<User, Void>(Model.of("Email")) {
|
||||
columns.add(new AbstractColumn<User, Void>(Model.of("Primary Email")) {
|
||||
|
||||
@Override
|
||||
public String getCssClass() {
|
||||
@ -213,7 +198,16 @@ public class UserListPage extends AdministrationPage {
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<User>> cellItem, String componentId,
|
||||
IModel<User> rowModel) {
|
||||
cellItem.add(new Label(componentId, rowModel.getObject().getEmail()));
|
||||
EmailAddress emailAddress = rowModel.getObject().getPrimaryEmailAddress();
|
||||
if (emailAddress != null) {
|
||||
Fragment fragment = new Fragment(componentId, "emailFrag", UserListPage.this);
|
||||
fragment.add(new Label("emailAddress", emailAddress.getValue()));
|
||||
fragment.add(new EmailAddressVerificationStatusBadge(
|
||||
"verificationStatus", Model.of(emailAddress)));
|
||||
cellItem.add(fragment);
|
||||
} else {
|
||||
cellItem.add(new Label(componentId, "<i>Not specified</i>").setEscapeModelStrings(false));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@ -303,14 +297,12 @@ public class UserListPage extends AdministrationPage {
|
||||
|
||||
@Override
|
||||
public Iterator<? extends User> iterator(long first, long count) {
|
||||
EntityCriteria<User> criteria = getCriteria();
|
||||
criteria.addOrder(Order.asc("name"));
|
||||
return OneDev.getInstance(UserManager.class).query(criteria, (int)first, (int)count).iterator();
|
||||
return getUserManager().query(query, (int)first, (int)count).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long calcSize() {
|
||||
return OneDev.getInstance(UserManager.class).count(getCriteria());
|
||||
return OneDev.getInstance(UserManager.class).count(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -348,6 +340,10 @@ public class UserListPage extends AdministrationPage {
|
||||
add(usersTable = new OneDataTable<User, Void>("users", columns, dataProvider,
|
||||
WebConstants.PAGE_SIZE, pagingHistorySupport));
|
||||
}
|
||||
|
||||
private UserManager getUserManager() {
|
||||
return OneDev.getInstance(UserManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
|
||||
@ -26,6 +26,7 @@ import io.onedev.server.web.page.admin.AdministrationPage;
|
||||
import io.onedev.server.web.page.admin.user.accesstoken.UserAccessTokenPage;
|
||||
import io.onedev.server.web.page.admin.user.authorization.UserAuthorizationsPage;
|
||||
import io.onedev.server.web.page.admin.user.avatar.UserAvatarPage;
|
||||
import io.onedev.server.web.page.admin.user.emailaddresses.UserEmailAddressesPage;
|
||||
import io.onedev.server.web.page.admin.user.membership.UserMembershipsPage;
|
||||
import io.onedev.server.web.page.admin.user.password.UserPasswordPage;
|
||||
import io.onedev.server.web.page.admin.user.profile.UserProfilePage;
|
||||
@ -66,6 +67,7 @@ public abstract class UserPage extends AdministrationPage {
|
||||
List<PageTab> tabs = new ArrayList<>();
|
||||
|
||||
tabs.add(new UserTab("Profile", "profile", UserProfilePage.class));
|
||||
tabs.add(new UserTab("Email Addresses", "mail", UserEmailAddressesPage.class));
|
||||
tabs.add(new UserTab("Edit Avatar", "avatar", UserAvatarPage.class));
|
||||
|
||||
tabs.add(new UserTab("Change Password", "password", UserPasswordPage.class));
|
||||
|
||||
@ -11,12 +11,13 @@ import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.commons.loader.AppLoader;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.util.Path;
|
||||
import io.onedev.server.util.PathNode;
|
||||
import io.onedev.server.web.editable.BeanContext;
|
||||
@ -24,11 +25,12 @@ import io.onedev.server.web.editable.BeanEditor;
|
||||
import io.onedev.server.web.page.admin.AdministrationPage;
|
||||
import io.onedev.server.web.page.admin.user.UserCssResourceReference;
|
||||
import io.onedev.server.web.page.admin.user.membership.UserMembershipsPage;
|
||||
import io.onedev.server.web.util.NewUserBean;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class NewUserPage extends AdministrationPage {
|
||||
|
||||
private User user = new User();
|
||||
private NewUserBean newUserBean = new NewUserBean();
|
||||
|
||||
private boolean continueToAdd;
|
||||
|
||||
@ -40,8 +42,7 @@ public class NewUserPage extends AdministrationPage {
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
BeanEditor editor = BeanContext.edit("editor", user,
|
||||
Sets.newHashSet(User.PROP_GIT_EMAIL, User.PROP_ALTERNATE_EMAILS), true);
|
||||
BeanEditor editor = BeanContext.edit("editor", newUserBean);
|
||||
|
||||
Form<?> form = new Form<Void>("form") {
|
||||
|
||||
@ -50,25 +51,43 @@ public class NewUserPage extends AdministrationPage {
|
||||
super.onSubmit();
|
||||
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
EmailAddressManager emailAddressManager = OneDev.getInstance(EmailAddressManager.class);
|
||||
|
||||
User userWithSameName = userManager.findByName(user.getName());
|
||||
User userWithSameName = userManager.findByName(newUserBean.getName());
|
||||
if (userWithSameName != null) {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_NAME)),
|
||||
"Login name already used by another account");
|
||||
}
|
||||
|
||||
User userWithSameEmail = userManager.findByEmail(user.getEmail());
|
||||
if (userWithSameEmail != null) {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_EMAIL)),
|
||||
"Email already used by another account");
|
||||
if (emailAddressManager.findByValue(newUserBean.getEmailAddress()) != null) {
|
||||
editor.error(new Path(new PathNode.Named(NewUserBean.PROP_EMAIL_ADDRESS)),
|
||||
"Email address already used by another user");
|
||||
}
|
||||
if (editor.isValid()){
|
||||
user.setPassword(AppLoader.getInstance(PasswordService.class).encryptPassword(user.getPassword()));
|
||||
userManager.save(user, null);
|
||||
User user = new User();
|
||||
user.setName(newUserBean.getName());
|
||||
user.setFullName(newUserBean.getFullName());
|
||||
user.setPassword(AppLoader.getInstance(PasswordService.class).encryptPassword(newUserBean.getPassword()));
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setValue(newUserBean.getEmailAddress());
|
||||
emailAddress.setOwner(user);
|
||||
emailAddress.setVerificationCode(null);
|
||||
|
||||
OneDev.getInstance(TransactionManager.class).run(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
userManager.save(user);
|
||||
emailAddressManager.save(emailAddress);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Session.get().success("New user created");
|
||||
if (continueToAdd) {
|
||||
user = new User();
|
||||
replace(BeanContext.edit("editor", user));
|
||||
newUserBean = new NewUserBean();
|
||||
replace(BeanContext.edit("editor", newUserBean));
|
||||
} else {
|
||||
setResponsePage(UserMembershipsPage.class, UserMembershipsPage.paramsOf(user));
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
<wicket:extend>
|
||||
<div wicket:id="content"></div>
|
||||
</wicket:extend>
|
||||
@ -0,0 +1,30 @@
|
||||
package io.onedev.server.web.page.admin.user.emailaddresses;
|
||||
|
||||
import org.apache.wicket.model.AbstractReadOnlyModel;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.component.user.emailaddresses.EmailAddressesPanel;
|
||||
import io.onedev.server.web.page.admin.user.UserPage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class UserEmailAddressesPage extends UserPage {
|
||||
|
||||
public UserEmailAddressesPage(PageParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
add(new EmailAddressesPanel("content", new AbstractReadOnlyModel<User>() {
|
||||
|
||||
@Override
|
||||
public User getObject() {
|
||||
return getUser();
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
<input wicket:id="filterGroups" class="form-control search" placeholder="Filter by name...">
|
||||
</div>
|
||||
<input wicket:id="addNew" type="hidden" class="form-control mr-3 mb-4">
|
||||
<a wicket:id="delete" class="btn btn-light-danger mb-4">Delete</a>
|
||||
<a wicket:id="delete" class="btn btn-light-danger mb-4">Delete <wicket:svg href="arrow" class="rotate-90 icon"/></a>
|
||||
</div>
|
||||
</div>
|
||||
<table wicket:id="memberships" class="memberships table"></table>
|
||||
|
||||
@ -21,9 +21,9 @@ public class UserPasswordPage extends UserPage {
|
||||
|
||||
if (getUser().getPassword().equals(User.EXTERNAL_MANAGED)) {
|
||||
String message;
|
||||
if (getUser().getSsoInfo().getConnector() != null) {
|
||||
if (getUser().getSsoConnector() != null) {
|
||||
message = "The user is currently authenticated via SSO provider '"
|
||||
+ getUser().getSsoInfo().getConnector()
|
||||
+ getUser().getSsoConnector()
|
||||
+ "', please change password there instead";
|
||||
} else {
|
||||
message = "The user is currently authenticated via external system, "
|
||||
|
||||
@ -30,9 +30,9 @@ public class UserTwoFactorAuthenticationPage extends UserPage {
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
if (getUser().getSsoInfo().getConnector() != null) {
|
||||
if (getUser().getSsoConnector() != null) {
|
||||
add(new Label("content", "This account is currently authenticated via SSO provider '"
|
||||
+ getUser().getSsoInfo().getConnector() + "', "
|
||||
+ getUser().getSsoConnector() + "', "
|
||||
+ "and two-factor authentication should be configured there")
|
||||
.add(AttributeAppender.append("class", "alert alert-light-warning alert-notice mb-0")));
|
||||
} else if (getUser().getTwoFactorAuthentication() != null) {
|
||||
|
||||
@ -132,6 +132,15 @@ ul.feedbackPanel>li:first-child:last-child {
|
||||
border-radius: 0 0.42rem 0.42rem 0;
|
||||
}
|
||||
|
||||
.form-group .feedbackPanel {
|
||||
margin: 0.8rem 0 !important;
|
||||
}
|
||||
.form-group .feedbackPanelERROR {
|
||||
background-color: inherit !important;
|
||||
border-left: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
}
|
||||
@ -166,6 +175,7 @@ tr.navigation>td {
|
||||
/* TABLE COMPONENTS */
|
||||
table .row-selector {
|
||||
width: 1px;
|
||||
padding-top: 14px;
|
||||
}
|
||||
table .row-selector .checkbox span {
|
||||
margin: 0;
|
||||
|
||||
@ -49,11 +49,26 @@
|
||||
<a href="#" class="dropdown-toggle user-info no-dropdown-caret topbar-link" data-toggle="dropdown">
|
||||
<img wicket:id="avatar">
|
||||
<span wicket:id="name" class="ml-2 d-none d-lg-inline"></span>
|
||||
<span wicket:id="warningIcon"><wicket:svg href="warning-o" class="icon text-warning ml-2"/></span>
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<wicket:enclosure child="hasUnverifiedLink">
|
||||
<div class="mx-3 my-2 alert alert-light-warning">
|
||||
You have unverified <a wicket:id="hasUnverifiedLink">email addresses</a>
|
||||
</div>
|
||||
</wicket:enclosure>
|
||||
<wicket:enclosure child="noPrimaryAddressLink">
|
||||
<div class="mx-3 my-2 alert alert-light-warning">
|
||||
Primary <a wicket:id="noPrimaryAddressLink">email address</a> not specified
|
||||
</div>
|
||||
</wicket:enclosure>
|
||||
<a wicket:id="myProfile" class="dropdown-item">
|
||||
<wicket:svg href="profile" class="icon mr-2"></wicket:svg>
|
||||
My Profile
|
||||
Profile
|
||||
</a>
|
||||
<a wicket:id="myEmailSetting" class="dropdown-item">
|
||||
<wicket:svg href="mail" class="icon mr-2"></wicket:svg>
|
||||
Email Addresses
|
||||
</a>
|
||||
<a wicket:id="myAvatar" class="dropdown-item">
|
||||
<wicket:svg href="avatar" class="icon mr-2"></wicket:svg>
|
||||
|
||||
@ -82,6 +82,7 @@ import io.onedev.server.web.page.help.IncompatibilitiesPage;
|
||||
import io.onedev.server.web.page.my.MyPage;
|
||||
import io.onedev.server.web.page.my.accesstoken.MyAccessTokenPage;
|
||||
import io.onedev.server.web.page.my.avatar.MyAvatarPage;
|
||||
import io.onedev.server.web.page.my.emailaddresses.MyEmailAddressesPage;
|
||||
import io.onedev.server.web.page.my.password.MyPasswordPage;
|
||||
import io.onedev.server.web.page.my.profile.MyProfilePage;
|
||||
import io.onedev.server.web.page.my.sshkeys.MySshKeysPage;
|
||||
@ -354,9 +355,24 @@ public abstract class LayoutPage extends BasePage {
|
||||
if (loginUser != null) {
|
||||
userInfo.add(new UserAvatar("avatar", loginUser));
|
||||
userInfo.add(new Label("name", loginUser.getDisplayName()));
|
||||
if (loginUser.getEmailAddresses().isEmpty()) {
|
||||
userInfo.add(new WebMarkupContainer("warningIcon"));
|
||||
userInfo.add(new WebMarkupContainer("hasUnverifiedLink").setVisible(false));
|
||||
userInfo.add(new ViewStateAwarePageLink<Void>("noPrimaryAddressLink", MyEmailAddressesPage.class));
|
||||
} else if (loginUser.getEmailAddresses().stream().anyMatch(it->!it.isVerified())) {
|
||||
userInfo.add(new WebMarkupContainer("warningIcon"));
|
||||
userInfo.add(new ViewStateAwarePageLink<Void>("hasUnverifiedLink", MyEmailAddressesPage.class));
|
||||
userInfo.add(new WebMarkupContainer("noPrimaryAddressLink").setVisible(false));
|
||||
} else {
|
||||
userInfo.add(new WebMarkupContainer("warningIcon").setVisible(false));
|
||||
userInfo.add(new WebMarkupContainer("hasUnverifiedLink").setVisible(false));
|
||||
userInfo.add(new WebMarkupContainer("noPrimaryAddressLink").setVisible(false));
|
||||
}
|
||||
} else {
|
||||
userInfo.add(new WebMarkupContainer("avatar"));
|
||||
userInfo.add(new WebMarkupContainer("name"));
|
||||
userInfo.add(new WebMarkupContainer("warningIcon"));
|
||||
userInfo.add(new WebMarkupContainer("warningLink"));
|
||||
}
|
||||
|
||||
WebMarkupContainer item;
|
||||
@ -364,6 +380,10 @@ public abstract class LayoutPage extends BasePage {
|
||||
if (getPage() instanceof MyProfilePage)
|
||||
item.add(AttributeAppender.append("class", "active"));
|
||||
|
||||
userInfo.add(item = new ViewStateAwarePageLink<Void>("myEmailSetting", MyEmailAddressesPage.class));
|
||||
if (getPage() instanceof MyEmailAddressesPage)
|
||||
item.add(AttributeAppender.append("class", "active"));
|
||||
|
||||
userInfo.add(item = new ViewStateAwarePageLink<Void>("myAvatar", MyAvatarPage.class));
|
||||
if (getPage() instanceof MyAvatarPage)
|
||||
item.add(AttributeAppender.append("class", "active"));
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
<wicket:extend>
|
||||
<div class="card m-2 m-sm-5">
|
||||
<div class="card-body">
|
||||
<div wicket:id="content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</wicket:extend>
|
||||
@ -0,0 +1,37 @@
|
||||
package io.onedev.server.web.page.my.emailaddresses;
|
||||
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.model.AbstractReadOnlyModel;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.component.user.emailaddresses.EmailAddressesPanel;
|
||||
import io.onedev.server.web.page.my.MyPage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MyEmailAddressesPage extends MyPage {
|
||||
|
||||
public MyEmailAddressesPage(PageParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
add(new EmailAddressesPanel("content", new AbstractReadOnlyModel<User>() {
|
||||
|
||||
@Override
|
||||
public User getObject() {
|
||||
return getLoginUser();
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component newTopbarTitle(String componentId) {
|
||||
return new Label(componentId, "My Email Addresses");
|
||||
}
|
||||
|
||||
}
|
||||
@ -23,9 +23,9 @@ public class MyPasswordPage extends MyPage {
|
||||
|
||||
if (getLoginUser().getPassword().equals(User.EXTERNAL_MANAGED)) {
|
||||
String message;
|
||||
if (getLoginUser().getSsoInfo().getConnector() != null) {
|
||||
if (getLoginUser().getSsoConnector() != null) {
|
||||
message = "You are currently authenticated via SSO provider '"
|
||||
+ getLoginUser().getSsoInfo().getConnector()
|
||||
+ getLoginUser().getSsoConnector()
|
||||
+ "', please change password there instead";
|
||||
} else {
|
||||
message = "You are currently authenticated via external system, "
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<wicket:extend>
|
||||
<div class="card m-2 m-sm-5">
|
||||
<div class="card-body">
|
||||
<div wicket:id="externalManagedNote" class="alert alert-notice alert-light-warning"></div>
|
||||
<div wicket:id="externalManagedNote" class="alert alert-warning alert-light-warning"></div>
|
||||
<div wicket:id="content"></div>
|
||||
<a wicket:id="delete" class="btn btn-light-danger">Delete</a>
|
||||
</div>
|
||||
|
||||
@ -18,7 +18,7 @@ import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.web.ajaxlistener.ConfirmClickListener;
|
||||
import io.onedev.server.web.component.modal.ModalLink;
|
||||
import io.onedev.server.web.component.modal.ModalPanel;
|
||||
import io.onedev.server.web.component.twofactorauthentication.TwoFactorAuthenticationSetupPanel;
|
||||
import io.onedev.server.web.component.user.twofactorauthentication.TwoFactorAuthenticationSetupPanel;
|
||||
import io.onedev.server.web.page.my.MyPage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@ -36,9 +36,9 @@ public class MyTwoFactorAuthenticationPage extends MyPage {
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
if (getLoginUser().getSsoInfo().getConnector() != null) {
|
||||
if (getLoginUser().getSsoConnector() != null) {
|
||||
add(new Label("content", "You are currently authenticated via SSO provider '"
|
||||
+ getLoginUser().getSsoInfo().getConnector() + "', "
|
||||
+ getLoginUser().getSsoConnector() + "', "
|
||||
+ "and two-factor authentication should be configured there")
|
||||
.add(AttributeAppender.append("class", "alert alert-light-warning alert-notice mb-0")));
|
||||
} else if (getLoginUser().getTwoFactorAuthentication() != null) {
|
||||
|
||||
@ -285,7 +285,7 @@ public class CommitOptionPanel extends Panel {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
while(newCommitId == null) {
|
||||
try {
|
||||
newCommitId = new BlobEdits(oldPaths, newBlobs).commit(repository, refName,
|
||||
|
||||
@ -17,12 +17,13 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.util.facade.UserFacade;
|
||||
import io.onedev.server.web.asset.emoji.Emojis;
|
||||
import io.onedev.server.web.avatar.AvatarManager;
|
||||
import io.onedev.server.web.page.project.commits.CommitDetailPage;
|
||||
@ -67,7 +68,7 @@ class LastCommitsResource extends AbstractResource {
|
||||
|
||||
AvatarManager avatarManager = OneDev.getInstance(AvatarManager.class);
|
||||
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
EmailAddressManager emailAddressManager = OneDev.getInstance(EmailAddressManager.class);
|
||||
Map<String, LastCommitInfo> map = new HashMap<>();
|
||||
for (Map.Entry<String, LastCommitsOfChildren.Value> entry: lastCommits.entrySet()) {
|
||||
LastCommitInfo info = new LastCommitInfo();
|
||||
@ -81,10 +82,15 @@ class LastCommitsResource extends AbstractResource {
|
||||
info.when = DateUtils.formatAge(value.getCommitDate());
|
||||
|
||||
PersonIdent author = value.getAuthor();
|
||||
UserFacade user = userManager.findFacadeByEmail(author.getEmailAddress());
|
||||
if (user != null) {
|
||||
info.authorName = user.getDisplayName();
|
||||
info.authorEmailAddress = user.getEmail();
|
||||
EmailAddress emailAddress = emailAddressManager.findByValue(author.getEmailAddress());
|
||||
if (emailAddress != null && emailAddress.isVerified()) {
|
||||
User owner = emailAddress.getOwner();
|
||||
info.authorName = owner.getDisplayName();
|
||||
EmailAddress primaryEmailAddress = owner.getPrimaryEmailAddress();
|
||||
if (primaryEmailAddress != null && primaryEmailAddress.isVerified())
|
||||
info.authorEmailAddress = primaryEmailAddress.getValue();
|
||||
else
|
||||
info.authorEmailAddress = author.getEmailAddress();
|
||||
} else {
|
||||
info.authorName = author.getName();
|
||||
info.authorEmailAddress = author.getEmailAddress();
|
||||
|
||||
@ -437,8 +437,7 @@ public class SourceViewPanel extends BlobViewPanel implements Positionable, Sear
|
||||
|
||||
for (User user: context.getProject().getAuthors(context.getBlobIdent().path, context.getCommit(),
|
||||
new LinearRange(range.getFromRow(), range.getToRow()))) {
|
||||
if (user.getEmail() != null)
|
||||
mentions.append("@").append(user.getName()).append(" ");
|
||||
mentions.append("@").append(user.getName()).append(" ");
|
||||
}
|
||||
|
||||
form.add(contentInput = new CommentInput("content", Model.of(mentions.toString()), true) {
|
||||
|
||||
@ -10,6 +10,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.wicket.Component;
|
||||
@ -152,10 +153,14 @@ public class NewPullRequestPage extends ProjectPage implements RevisionDiff.Anno
|
||||
|
||||
private String suggestSourceBranch() {
|
||||
User user = getLoginUser();
|
||||
Collection<String> verifiedEmailAddresses = user.getEmailAddresses().stream()
|
||||
.filter(it->it.isVerified())
|
||||
.map(it->it.getValue())
|
||||
.collect(Collectors.toSet());
|
||||
List<Pair<String, Integer>> branchUpdates = new ArrayList<>();
|
||||
for (RefInfo refInfo: getProject().getBranchRefInfos()) {
|
||||
RevCommit commit = (RevCommit) refInfo.getPeeledObj();
|
||||
if (commit.getAuthorIdent().getEmailAddress().equals(user.getEmail()))
|
||||
if (verifiedEmailAddresses.contains(commit.getAuthorIdent().getEmailAddress().toLowerCase()))
|
||||
branchUpdates.add(new Pair<>(GitUtils.ref2branch(refInfo.getRef().getName()), commit.getCommitTime()));
|
||||
}
|
||||
branchUpdates.sort(Comparator.comparing(Pair::getSecond));
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
<wicket:extend>
|
||||
<div wicket:id="successful" class="alert alert-light-success font-size-lg font-weight-bolder">
|
||||
<wicket:svg href="tick-circle" class="icon"/>
|
||||
Your email address is now verified
|
||||
</div>
|
||||
<div wicket:id="failed" class="alert alert-light-danger">
|
||||
<wicket:svg href="times-circle" class="icon"/>
|
||||
Failed to verify your email address
|
||||
</div>
|
||||
<a wicket:id="goHome" class="btn btn-primary">Back To Home</a>
|
||||
</wicket:extend>
|
||||
@ -0,0 +1,75 @@
|
||||
package io.onedev.server.web.page.simple.security;
|
||||
|
||||
import org.apache.wicket.markup.html.WebMarkupContainer;
|
||||
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.web.page.simple.SimplePage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class EmailAddressVerificationPage extends SimplePage {
|
||||
|
||||
private final String PARAM_EMAIL_ADDRESS = "emailAddress";
|
||||
|
||||
private final String PARAM_VERIFICATION_CODE = "verificationCode";
|
||||
|
||||
private final IModel<EmailAddress> emailAddressModel;
|
||||
|
||||
public EmailAddressVerificationPage(PageParameters params) {
|
||||
super(params);
|
||||
|
||||
Long emailAddressId = params.get(PARAM_EMAIL_ADDRESS).toLong();
|
||||
emailAddressModel = new LoadableDetachableModel<EmailAddress>() {
|
||||
|
||||
@Override
|
||||
protected EmailAddress load() {
|
||||
return getEmailAddressManager().load(emailAddressId);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
EmailAddress emailAddress = emailAddressModel.getObject();
|
||||
String verificationCode = params.get(PARAM_VERIFICATION_CODE).toString();
|
||||
|
||||
if (verificationCode.equals(emailAddress.getVerficationCode())) {
|
||||
emailAddress.setVerificationCode(null);
|
||||
getEmailAddressManager().save(emailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private EmailAddressManager getEmailAddressManager() {
|
||||
return OneDev.getInstance(EmailAddressManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
EmailAddress emailAddress = emailAddressModel.getObject();
|
||||
add(new WebMarkupContainer("successful").setVisible(emailAddress.isVerified()));
|
||||
add(new WebMarkupContainer("failed").setVisible(!emailAddress.isVerified()));
|
||||
add(new BookmarkablePageLink<Void>("goHome", getApplication().getHomePage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach() {
|
||||
emailAddressModel.detach();
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Email Address Verification";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSubTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -39,7 +39,7 @@ import io.onedev.server.model.support.administration.sso.SsoConnector;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.security.realm.PasswordAuthorizingRealm;
|
||||
import io.onedev.server.web.component.link.ViewStateAwarePageLink;
|
||||
import io.onedev.server.web.component.twofactorauthentication.TwoFactorAuthenticationSetupPanel;
|
||||
import io.onedev.server.web.component.user.twofactorauthentication.TwoFactorAuthenticationSetupPanel;
|
||||
import io.onedev.server.web.page.simple.SimpleCssResourceReference;
|
||||
import io.onedev.server.web.page.simple.SimplePage;
|
||||
|
||||
@ -215,7 +215,7 @@ public class LoginPage extends SimplePage {
|
||||
rememberMeManager.onSuccessfulLogin(SecurityUtils.getSubject(), token, user);
|
||||
|
||||
continueToOriginalDestination();
|
||||
setResponsePage(getApplication().getHomePage());
|
||||
throw new RestartResponseException(getApplication().getHomePage());
|
||||
}
|
||||
|
||||
private void newTwoFactorAuthenticationSetup(Long userId) {
|
||||
|
||||
@ -21,6 +21,7 @@ import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.notification.MailManager;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
@ -76,9 +77,9 @@ public class PasswordResetPage extends SimplePage {
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
User user = userManager.findByName(loginNameOrEmail);
|
||||
if (user == null)
|
||||
user = userManager.findByEmail(loginNameOrEmail);
|
||||
user = userManager.findByVerifiedEmailAddress(loginNameOrEmail);
|
||||
if (user == null) {
|
||||
throw new ExplicitException("No user found with login name or email: " + loginNameOrEmail);
|
||||
throw new ExplicitException("No user found with login name or verified email: " + loginNameOrEmail);
|
||||
} else {
|
||||
SettingManager settingManager = OneDev.getInstance(SettingManager.class);
|
||||
if (settingManager.getMailSetting() != null) {
|
||||
@ -102,14 +103,27 @@ public class PasswordResetPage extends SimplePage {
|
||||
+ "%s",
|
||||
user.getDisplayName(), user.getName(), serverUrl, password);
|
||||
|
||||
String emailAddressValue;
|
||||
if (loginNameOrEmail.contains("@")) {
|
||||
emailAddressValue = loginNameOrEmail;
|
||||
} else {
|
||||
EmailAddress emailAddress = user.getPrimaryEmailAddress();
|
||||
if (emailAddress == null)
|
||||
throw new ExplicitException("Primary email address not specified");
|
||||
else if (!emailAddress.isVerified())
|
||||
throw new ExplicitException("Your primary email address is not verified");
|
||||
else
|
||||
emailAddressValue = emailAddress.getValue();
|
||||
}
|
||||
|
||||
mailManager.sendMail(
|
||||
settingManager.getMailSetting(),
|
||||
Arrays.asList(user.getEmail()),
|
||||
Arrays.asList(emailAddressValue),
|
||||
Lists.newArrayList(), Lists.newArrayList(),
|
||||
"[Password Reset] Your OneDev Password Has Been Reset",
|
||||
htmlBody, textBody, null, null);
|
||||
|
||||
return "Please check your email " + user.getEmail() + " for the reset password";
|
||||
return "Please check your email " + emailAddressValue + " for the reset password";
|
||||
} else {
|
||||
throw new ExplicitException("Unable to send password reset email as smtp setting is not defined");
|
||||
}
|
||||
|
||||
@ -8,13 +8,14 @@ import org.apache.wicket.markup.html.form.SubmitLink;
|
||||
import org.apache.wicket.markup.html.link.Link;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.commons.loader.AppLoader;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.EmailAddressManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.Path;
|
||||
import io.onedev.server.util.PathNode;
|
||||
@ -23,6 +24,7 @@ import io.onedev.server.web.editable.BeanEditor;
|
||||
import io.onedev.server.web.page.my.avatar.MyAvatarPage;
|
||||
import io.onedev.server.web.page.project.ProjectListPage;
|
||||
import io.onedev.server.web.page.simple.SimplePage;
|
||||
import io.onedev.server.web.util.NewUserBean;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class SignUpPage extends SimplePage {
|
||||
@ -40,9 +42,8 @@ public class SignUpPage extends SimplePage {
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
User user = new User();
|
||||
BeanEditor editor = BeanContext.edit("editor", user,
|
||||
Sets.newHashSet(User.PROP_GIT_EMAIL, User.PROP_ALTERNATE_EMAILS), true);
|
||||
NewUserBean newUserBean = new NewUserBean();
|
||||
BeanEditor editor = BeanContext.edit("editor", newUserBean);
|
||||
|
||||
Form<?> form = new Form<Void>("form") {
|
||||
|
||||
@ -51,19 +52,38 @@ public class SignUpPage extends SimplePage {
|
||||
super.onSubmit();
|
||||
|
||||
UserManager userManager = OneDev.getInstance(UserManager.class);
|
||||
User userWithSameName = userManager.findByName(user.getName());
|
||||
User userWithSameName = userManager.findByName(newUserBean.getName());
|
||||
if (userWithSameName != null) {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_NAME)),
|
||||
"Login name already used by another account");
|
||||
}
|
||||
User userWithSameEmail = userManager.findByEmail(user.getEmail());
|
||||
if (userWithSameEmail != null) {
|
||||
editor.error(new Path(new PathNode.Named(User.PROP_EMAIL)),
|
||||
"Email already used by another account");
|
||||
|
||||
EmailAddressManager emailAddressManager = OneDev.getInstance(EmailAddressManager.class);
|
||||
|
||||
if (emailAddressManager.findByValue(newUserBean.getEmailAddress()) != null) {
|
||||
editor.error(new Path(new PathNode.Named(NewUserBean.PROP_EMAIL_ADDRESS)),
|
||||
"Email address already used by another user");
|
||||
}
|
||||
if (editor.isValid()) {
|
||||
user.setPassword(AppLoader.getInstance(PasswordService.class).encryptPassword(user.getPassword()));
|
||||
userManager.save(user, null);
|
||||
User user = new User();
|
||||
user.setName(newUserBean.getName());
|
||||
user.setFullName(newUserBean.getFullName());
|
||||
user.setPassword(AppLoader.getInstance(PasswordService.class).encryptPassword(newUserBean.getPassword()));
|
||||
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setValue(newUserBean.getEmailAddress());
|
||||
emailAddress.setOwner(user);
|
||||
|
||||
OneDev.getInstance(TransactionManager.class).run(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
userManager.save(user);
|
||||
emailAddressManager.save(emailAddress);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Session.get().success("Welcome to OneDev");
|
||||
SecurityUtils.getSubject().runAs(user.getPrincipals());
|
||||
setResponsePage(MyAvatarPage.class);
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
package io.onedev.server.web.util;
|
||||
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.editable.annotation.Editable;
|
||||
|
||||
@Editable
|
||||
public class NewUserBean extends User {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String PROP_EMAIL_ADDRESS = "emailAddress";
|
||||
|
||||
private String emailAddress;
|
||||
|
||||
@Editable(order=1000)
|
||||
@NotEmpty
|
||||
@Email
|
||||
public String getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
public void setEmailAddress(String emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,7 +15,6 @@ import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
@ -51,7 +50,6 @@ import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.administration.GroovyScript;
|
||||
import io.onedev.server.model.support.build.JobSecret;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.security.permission.AccessProject;
|
||||
import io.onedev.server.util.interpolative.VariableInterpolator;
|
||||
@ -223,14 +221,7 @@ public class SuggestionUtils {
|
||||
matchWith = matchWith.toLowerCase();
|
||||
List<InputSuggestion> suggestions = new ArrayList<>();
|
||||
|
||||
EntityCriteria<User> criteria = EntityCriteria.of(User.class);
|
||||
criteria.add(Restrictions.or(
|
||||
Restrictions.ilike(User.PROP_NAME, "%" + matchWith + "%"),
|
||||
Restrictions.ilike(User.PROP_EMAIL, "%" + matchWith + "%"),
|
||||
Restrictions.ilike(User.PROP_FULL_NAME, "%" + matchWith + "%")));
|
||||
criteria.add(Restrictions.gt("id", 0L));
|
||||
|
||||
for (User user: OneDev.getInstance(UserManager.class).query(criteria)) {
|
||||
for (User user: OneDev.getInstance(UserManager.class).query(matchWith, 0, InputAssistBehavior.MAX_SUGGESTIONS)) {
|
||||
LinearRange match = LinearRange.match(user.getName(), matchWith);
|
||||
String description;
|
||||
if (!user.getDisplayName().equals(user.getName()))
|
||||
@ -238,8 +229,6 @@ public class SuggestionUtils {
|
||||
else
|
||||
description = null;
|
||||
suggestions.add(new InputSuggestion(user.getName(), description, match));
|
||||
if (suggestions.size() >= InputAssistBehavior.MAX_SUGGESTIONS)
|
||||
break;
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import org.junit.Test;
|
||||
|
||||
import io.onedev.server.event.RefUpdated;
|
||||
import io.onedev.server.git.AbstractGitTest;
|
||||
import io.onedev.server.model.EmailAddress;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
|
||||
@ -44,7 +45,14 @@ public class CommitQueryTest extends AbstractGitTest {
|
||||
RefUpdated event = new RefUpdated(project, "refs/heads/master", oldCommitId, newCommitId);
|
||||
|
||||
User user = new User();
|
||||
user.setEmail(CommitQueryTest.this.user.getEmailAddress());
|
||||
EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setGit(true);
|
||||
emailAddress.setPrimary(true);
|
||||
emailAddress.setOwner(user);
|
||||
emailAddress.setVerificationCode(null);
|
||||
emailAddress.setValue(CommitQueryTest.this.user.getEmailAddress());
|
||||
user.getEmailAddresses().add(emailAddress);
|
||||
|
||||
User.push(user);
|
||||
try {
|
||||
assertTrue(CommitQuery.parse(project, null).matches(event));
|
||||
|
||||
@ -94,7 +94,7 @@ public class ImportUtils {
|
||||
if (userNode.hasNonNull("email"))
|
||||
email = userNode.get("email").asText(null);
|
||||
if (email != null)
|
||||
userOpt = Optional.ofNullable(OneDev.getInstance(UserManager.class).findByEmail(email));
|
||||
userOpt = Optional.ofNullable(OneDev.getInstance(UserManager.class).findByVerifiedEmailAddress(email));
|
||||
else
|
||||
userOpt = Optional.empty();
|
||||
users.put(login, userOpt);
|
||||
|
||||
@ -85,7 +85,7 @@ public class ImportUtils {
|
||||
String apiEndpoint = importSource.getApiEndpoint("/users/" + login);
|
||||
String email = get(client, apiEndpoint, logger).get("email").asText(null);
|
||||
if (email != null)
|
||||
userOpt = Optional.ofNullable(OneDev.getInstance(UserManager.class).findByEmail(email));
|
||||
userOpt = Optional.ofNullable(OneDev.getInstance(UserManager.class).findByVerifiedEmailAddress(email));
|
||||
else
|
||||
userOpt = Optional.empty();
|
||||
users.put(login, userOpt);
|
||||
|
||||
@ -99,7 +99,7 @@ public class ImportUtils {
|
||||
if (email == null && userNode.hasNonNull("public_email"))
|
||||
email = userNode.get("public_email").asText(null);
|
||||
if (email != null)
|
||||
userOpt = Optional.ofNullable(OneDev.getInstance(UserManager.class).findByEmail(email));
|
||||
userOpt = Optional.ofNullable(OneDev.getInstance(UserManager.class).findByVerifiedEmailAddress(email));
|
||||
else
|
||||
userOpt = Optional.empty();
|
||||
users.put(userId, userOpt);
|
||||
|
||||
@ -301,7 +301,7 @@ public class ImportUtils {
|
||||
String email = getEmail(reporterNode);
|
||||
String login = reporterNode.get("login").asText();
|
||||
if (email != null) {
|
||||
User user = OneDev.getInstance(UserManager.class).findByEmail(email);
|
||||
User user = OneDev.getInstance(UserManager.class).findByVerifiedEmailAddress(email);
|
||||
if (user != null) {
|
||||
issue.setSubmitter(user);
|
||||
} else {
|
||||
@ -453,7 +453,7 @@ public class ImportUtils {
|
||||
extraIssueInfo.put(fieldName, fullName);
|
||||
} else {
|
||||
if (email != null) {
|
||||
User user = OneDev.getInstance(UserManager.class).findByEmail(email);
|
||||
User user = OneDev.getInstance(UserManager.class).findByVerifiedEmailAddress(email);
|
||||
if (user != null) {
|
||||
fieldValue = user.getName();
|
||||
} else {
|
||||
@ -494,7 +494,7 @@ public class ImportUtils {
|
||||
String email = getEmail(valueNode);
|
||||
|
||||
if (email != null) {
|
||||
User user = OneDev.getInstance(UserManager.class).findByEmail(email);
|
||||
User user = OneDev.getInstance(UserManager.class).findByVerifiedEmailAddress(email);
|
||||
if (user != null) {
|
||||
fieldValue = user.getName();
|
||||
} else {
|
||||
@ -727,7 +727,7 @@ public class ImportUtils {
|
||||
String email = getEmail(authorNode);
|
||||
String login = authorNode.get("login").asText();
|
||||
if (email != null) {
|
||||
User user = OneDev.getInstance(UserManager.class).findByEmail(email);
|
||||
User user = OneDev.getInstance(UserManager.class).findByVerifiedEmailAddress(email);
|
||||
if (user != null) {
|
||||
comment.setUser(user);
|
||||
} else {
|
||||
|
||||
@ -74,7 +74,7 @@ public class GitHubConnector extends OpenIdConnector {
|
||||
throw new AuthenticationException("A public email is required");
|
||||
String fullName = (String) json.get("name");
|
||||
|
||||
return new SsoAuthenticated(userName, userName, email, fullName, null, null, this);
|
||||
return new SsoAuthenticated(userName, email, fullName, null, null, this);
|
||||
} else {
|
||||
throw buildException(UserInfoErrorResponse.parse(httpResponse).getErrorObject());
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user