• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IQSS / dataverse / #22693

03 Jul 2024 01:09PM CUT coverage: 20.626% (-0.09%) from 20.716%
#22693

push

github

web-flow
Merge pull request #10664 from IQSS/develop

merge develop into master for 6.3

195 of 1852 new or added lines in 82 files covered. (10.53%)

72 existing lines in 33 files now uncovered.

17335 of 84043 relevant lines covered (20.63%)

0.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/MergeInAccountCommand.java
1
/*
2

3
 * To change this license header, choose License Headers in Project Properties.
4
 * To change this template file, choose Tools | Templates
5
 * and open the template in the editor.
6
 */
7
package edu.harvard.iq.dataverse.engine.command.impl;
8

9
import edu.harvard.iq.dataverse.DatasetLock;
10
import edu.harvard.iq.dataverse.DatasetVersionUser;
11
import edu.harvard.iq.dataverse.DvObject;
12
import edu.harvard.iq.dataverse.GuestbookResponse;
13
import edu.harvard.iq.dataverse.RoleAssignment;
14
import edu.harvard.iq.dataverse.UserNotification;
15
import edu.harvard.iq.dataverse.authorization.AuthenticatedUserLookup;
16
import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUser;
17
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
18
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
19
import edu.harvard.iq.dataverse.batch.util.LoggingUtil;
20
import edu.harvard.iq.dataverse.confirmemail.ConfirmEmailData;
21
import edu.harvard.iq.dataverse.engine.command.AbstractVoidCommand;
22
import edu.harvard.iq.dataverse.engine.command.CommandContext;
23
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
24
import edu.harvard.iq.dataverse.engine.command.RequiredPermissions;
25
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
26
import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
27
import edu.harvard.iq.dataverse.search.IndexResponse;
28
import edu.harvard.iq.dataverse.search.savedsearch.SavedSearch;
29
import edu.harvard.iq.dataverse.workflows.WorkflowComment;
30
import java.io.IOException;
31
import java.util.List;
32
import java.util.logging.Logger;
33
import org.apache.solr.client.solrj.SolrServerException;
34

35
/**
36
 * Merges one account into another.
37
 * 
38
 * @author matthew
39
 */
40

41
@RequiredPermissions({})
42
public class MergeInAccountCommand extends AbstractVoidCommand {
43
    final AuthenticatedUser consumedAU;
44
    final AuthenticatedUser ongoingAU;
45

46
    private static final Logger logger = Logger.getLogger(MergeInAccountCommand.class.getCanonicalName());
×
47
    
48
    public MergeInAccountCommand(DataverseRequest createDataverseRequest, AuthenticatedUser consumedAuthenticatedUser, AuthenticatedUser ongoingAU) {
49
        super(
×
50
            createDataverseRequest,
51
            (DvObject) null
52
        );
53
        consumedAU = consumedAuthenticatedUser;
×
54
        this.ongoingAU = ongoingAU;
×
55
    }
×
56
    
57
    @Override
58
    protected void executeImpl(CommandContext ctxt) throws CommandException {
59

60
        if (consumedAU.getId() == ongoingAU.getId()) {
×
61
            throw new IllegalCommandException("You cannot merge an account into itself.", this);
×
62
        }
63

64
        if (consumedAU.isDeactivated() && !ongoingAU.isDeactivated() || !consumedAU.isDeactivated() && ongoingAU.isDeactivated()) {
×
65
            throw new IllegalCommandException("User accounts can only be merged if they are either both active or both deactivated.", this);
×
66
        }
67

68
        List<RoleAssignment> baseRAList = ctxt.roleAssignees().getAssignmentsFor(ongoingAU.getIdentifier());
×
69
        List<RoleAssignment> consumedRAList = ctxt.roleAssignees().getAssignmentsFor(consumedAU.getIdentifier());
×
70
        
71
        for(RoleAssignment cra : consumedRAList) {
×
72
            if(cra.getAssigneeIdentifier().charAt(0) == '@') {
×
73
                
74
                boolean willDelete = false;
×
75
                for(RoleAssignment bra : baseRAList) {
×
76
                    //Matching on the id not the whole DVObject as I'm suspicious of dvobject equality
77
                    if( bra.getDefinitionPoint().getId().equals(cra.getDefinitionPoint().getId())
×
78
                        && bra.getRole().equals(cra.getRole())) { 
×
79
                        willDelete = true; //more or less a skip, as we run a delete query afterwards
×
80
                    }
81
                }
×
82
                if(!willDelete) {
×
83
                    cra.setAssigneeIdentifier(ongoingAU.getIdentifier());
×
84
                    ctxt.em().merge(cra);
×
85
                    IndexResponse indexResponse = ctxt.solrIndex().indexPermissionsForOneDvObject(cra.getDefinitionPoint());
×
86
                    try {
87
                        ctxt.index().indexDvObject(cra.getDefinitionPoint());
×
88
                    } catch (IOException | SolrServerException e) {
×
89
                        String failureLogText = "Post merge account dataset indexing failed. You can kickoff a re-index of this dataset with: \r\n curl http://localhost:8080/api/admin/index/datasets/" + cra.getDefinitionPoint().getId().toString();
×
90
                        failureLogText += "\r\n" + e.getLocalizedMessage();
×
91
                        LoggingUtil.writeOnSuccessFailureLog(this, failureLogText, cra.getDefinitionPoint());
×
92

93
                    }                   
×
94
                } // no else here because the any willDelete == true will happen in the named query below.
95
            } else {
×
96
                throw new IllegalCommandException("Original userIdentifier provided does not seem to be an AuthenticatedUser", this);
×
97
            }
98
        }
×
99
        
100
        //Delete role assignments for consumedIdentifier not merged, e.g. duplicates
101
        int resultCount = ctxt.em().createNamedQuery("RoleAssignment.deleteAllByAssigneeIdentifier", RoleAssignment.class).
×
102
                        setParameter("assigneeIdentifier", consumedAU.getIdentifier())
×
103
                        .executeUpdate();
×
104
        
105
        // DatasetVersionUser
106
        for (DatasetVersionUser user : ctxt.datasetVersion().getDatasetVersionUsersByAuthenticatedUser(consumedAU)) {
×
107
            user.setAuthenticatedUser(ongoingAU);
×
108
            ctxt.em().merge(user);
×
109
        }
×
110
        
111
        //DatasetLocks
112
        for (DatasetLock lock : ctxt.datasets().getDatasetLocksByUser(consumedAU)) {
×
113
            lock.setUser(ongoingAU);
×
114
            ctxt.em().merge(lock);
×
115
        }
×
116

117
        //DVObjects creator and release
118
        for (DvObject dvo : ctxt.dvObjects().findByAuthenticatedUserId(consumedAU)) {
×
119
            if (dvo.getCreator().equals(consumedAU)){
×
120
                dvo.setCreator(ongoingAU);
×
121
            }
122
            if (dvo.getReleaseUser() != null &&  dvo.getReleaseUser().equals(consumedAU)){
×
123
                dvo.setReleaseUser(ongoingAU);
×
124
            }
125
            ctxt.em().merge(dvo);
×
126
        }
×
127
        
128
        //GuestbookResponse
129
        for (GuestbookResponse gbr : ctxt.responses().findByAuthenticatedUserId(consumedAU)) {
×
130
            gbr.setAuthenticatedUser(ongoingAU);
×
131
            ctxt.em().merge(gbr);
×
132
        }
×
133
        
134
        //UserNotification
135
        for (UserNotification note : ctxt.notifications().findByUser(consumedAU.getId())) {
×
136
            note.setUser(ongoingAU);
×
137
            ctxt.em().merge(note);
×
138
        }
×
139
        
140
        //UserNotification
141
        for (UserNotification note : ctxt.notifications().findByRequestor(consumedAU.getId())) {
×
142
            note.setRequestor(ongoingAU);
×
143
            ctxt.em().merge(note);
×
144
        }
×
145
        
146
        // Set<ExplicitGroup>
147
        //for () 
148
        
149
        //SavedSearch
150
        for (SavedSearch search : ctxt.savedSearches().findByAuthenticatedUser(consumedAU)) {
×
151
            search.setCreator(ongoingAU);
×
152
            ctxt.em().merge(search);
×
153
        }
×
154
        
155
        //Workflow Comments
156
        for (WorkflowComment wc : ctxt.authentication().getWorkflowCommentsByAuthenticatedUser(consumedAU)) {
×
157
            wc.setAuthenticatedUser(ongoingAU);
×
158
            ctxt.em().merge(wc);
×
159
        }
×
160
        
161

162
        
163
        //ConfirmEmailData  
164
        
165
        // todo: the deletion should be handed down to the service!
166
        ConfirmEmailData confirmEmailData = ctxt.confirmEmail().findSingleConfirmEmailDataByUser(consumedAU); 
×
167
        if (confirmEmailData != null){
×
168
            ctxt.em().remove(confirmEmailData);
×
169
        }
170

171
        
172
        //Access Request is not an entity. have to update with native query
173
        
174
        ctxt.em().createNativeQuery("UPDATE fileaccessrequests SET authenticated_user_id="+ongoingAU.getId()+" WHERE authenticated_user_id="+consumedAU.getId()).executeUpdate();
×
175
        
176
        ctxt.em().createNativeQuery("Delete from OAuth2TokenData where user_id ="+consumedAU.getId()).executeUpdate();
×
177
        
NEW
178
        ctxt.em().createNativeQuery("DELETE FROM explicitgroup_authenticateduser consumed USING explicitgroup_authenticateduser ongoing WHERE consumed.containedauthenticatedusers_id="+ongoingAU.getId()+" AND ongoing.containedauthenticatedusers_id="+consumedAU.getId()).executeUpdate();
×
UNCOV
179
        ctxt.em().createNativeQuery("UPDATE explicitgroup_authenticateduser SET containedauthenticatedusers_id="+ongoingAU.getId()+" WHERE containedauthenticatedusers_id="+consumedAU.getId()).executeUpdate();
×
180
        
181
        ctxt.actionLog().changeUserIdentifierInHistory(consumedAU.getIdentifier(), ongoingAU.getIdentifier());
×
182
        
183
        //delete:
184
        //  builtin user - if applicable
185
        //  authenticated user
186
        //  AuthenticatedUserLookup
187
        //  apiToken
188
        ApiToken toRemove = ctxt.authentication().findApiTokenByUser(consumedAU);
×
189
        if(null != toRemove) { //not all users have apiTokens
×
190
            ctxt.em().remove(toRemove);
×
191
        }
192
        AuthenticatedUserLookup consumedAUL = consumedAU.getAuthenticatedUserLookup();
×
193
        ctxt.em().remove(consumedAUL);
×
194
        ctxt.em().remove(consumedAU);
×
195
        BuiltinUser consumedBuiltinUser = ctxt.builtinUsers().findByUserName(consumedAU.getUserIdentifier());
×
196
        if (consumedBuiltinUser != null) {
×
197
            ctxt.builtinUsers().removeUser(consumedBuiltinUser.getUserName());
×
198
        }
199
        
200
        
201
    }
×
202
    
203
    @Override
204
    public String describe() {
205
        return "User " + consumedAU.getUserIdentifier() + " (type: " + consumedAU.getAuthenticatedUserLookup().getAuthenticationProviderId() + " | persistentUserId: "
×
206
                + consumedAU.getAuthenticatedUserLookup().getPersistentUserId() +
×
207
                "; Name: "+ consumedAU.getFirstName() + " " + consumedAU.getLastName() +"; Institution: "  + consumedAU.getAffiliation() + "; Email: " + consumedAU.getEmail() + ") merged into " +ongoingAU.getUserIdentifier();
×
208
    }
209
    
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc