Project

General

Profile

Download (11.2 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.model.permission;
11

    
12
import java.util.Collection;
13
import java.util.HashSet;
14
import java.util.Set;
15

    
16
import javax.persistence.Column;
17
import javax.persistence.Entity;
18
import javax.persistence.FetchType;
19
import javax.persistence.ManyToMany;
20
import javax.persistence.OneToOne;
21
import javax.persistence.Table;
22
import javax.persistence.Transient;
23
import javax.validation.constraints.NotNull;
24
import javax.validation.constraints.Pattern;
25
import javax.xml.bind.annotation.XmlAccessType;
26
import javax.xml.bind.annotation.XmlAccessorType;
27
import javax.xml.bind.annotation.XmlElement;
28
import javax.xml.bind.annotation.XmlElementWrapper;
29
import javax.xml.bind.annotation.XmlIDREF;
30
import javax.xml.bind.annotation.XmlRootElement;
31
import javax.xml.bind.annotation.XmlSchemaType;
32
import javax.xml.bind.annotation.XmlTransient;
33
import javax.xml.bind.annotation.XmlType;
34

    
35
import org.apache.commons.lang3.StringUtils;
36
import org.apache.log4j.Logger;
37
import org.hibernate.annotations.Cascade;
38
import org.hibernate.annotations.CascadeType;
39
import org.hibernate.envers.Audited;
40
import org.hibernate.envers.NotAudited;
41
import org.hibernate.search.annotations.Analyze;
42
import org.hibernate.search.annotations.Field;
43
import org.hibernate.search.annotations.IndexedEmbedded;
44
import org.springframework.security.core.Authentication;
45
import org.springframework.security.core.GrantedAuthority;
46
import org.springframework.security.core.context.SecurityContextHolder;
47
import org.springframework.security.core.userdetails.UserDetails;
48

    
49
import eu.etaxonomy.cdm.model.agent.Person;
50
import eu.etaxonomy.cdm.model.common.CdmBase;
51

    
52
@XmlAccessorType(XmlAccessType.FIELD)
53
@XmlType(name = "User", propOrder = {
54
    "username",
55
    "password",
56
    "salt",
57
    "emailAddress",
58
    "grantedAuthorities",
59
    "authorities",
60
    "groups",
61
    "enabled",
62
    "accountNonExpired",
63
    "credentialsNonExpired",
64
    "accountNonLocked",
65
    "person"
66
})
67
@XmlRootElement(name = "User")
68
@Entity
69
//@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
70
//@Indexed(index = "eu.etaxonomy.cdm.model.common.User")
71
@Audited
72
@Table(name = "UserAccount")
73
public class User extends CdmBase implements UserDetails {
74
    private static final long serialVersionUID = 6582191171369439163L;
75
    private static final Logger logger = Logger.getLogger(User.class);
76

    
77
    public static final String USERNAME_REGEX = "[A-Za-z0-9_\\.\\-]+";
78

    
79
 // **************************** FACTORY *****************************************/
80

    
81
    public static User NewInstance(String username, String pwd){
82
        User user = new User();
83
        user.setUsername(username);
84
        user.setPassword(pwd);
85

    
86
        user.setAccountNonExpired(true);
87
        user.setAccountNonLocked(true);
88
        user.setCredentialsNonExpired(true);
89
        user.setEnabled(true);
90

    
91
        return user;
92
    }
93

    
94
    public static User NewInstance(String personTitle, String username, String pwd){
95
        User user = NewInstance(username, pwd);
96
        Person userPerson = Person.NewTitledInstance(personTitle);
97
        user.setPerson(userPerson);
98

    
99
        return user;
100
    }
101

    
102
//***************************** Fields *********************** /
103

    
104
    @XmlElement(name = "Username")
105
    @Column(unique = true)
106
    @Field(analyze = Analyze.NO)
107
    @NotNull
108
    @Pattern(regexp=USERNAME_REGEX)
109
    protected String username;
110

    
111
    /**
112
     * a salted, MD5 encoded hash of the plain text password
113
     */
114
    @XmlElement(name = "Password")
115
    @NotAudited
116
    protected String password;
117

    
118

    
119
    /**
120
     * The salt for password hashing.
121
     * @see https://dev.e-taxonomy.eu/redmine/issues/7210
122
     * @see https://code-bude.net/2015/03/30/grundlagen-sicheres-passwort-hashing-mit-salts/
123
     */
124
    @XmlElement(name = "Salt")
125
    @NotAudited
126
    protected String salt;
127

    
128
    @XmlElement(name = "EmailAddress")
129
    protected String emailAddress;
130

    
131
    @XmlElementWrapper(name = "GrantedAuthorities")
132
    @XmlElement(name = "GrantedAuthority", type = GrantedAuthorityImpl.class)
133
    @XmlIDREF
134
    @XmlSchemaType(name = "IDREF")
135
    @ManyToMany(fetch = FetchType.LAZY, targetEntity = GrantedAuthorityImpl.class)
136
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.REFRESH}) // see #2414 (Group updating doesn't work)
137
    @NotAudited
138
    protected Set<GrantedAuthority> grantedAuthorities = new HashSet<>();  //authorities of this user only
139

    
140
    @XmlElementWrapper(name = "Groups")
141
    @XmlElement(name = "Group")
142
    @XmlIDREF
143
    @XmlSchemaType(name = "IDREF")
144
    @ManyToMany(fetch = FetchType.LAZY)
145
    @IndexedEmbedded(depth = 1)
146
    @NotAudited
147
    protected Set<Group> groups = new HashSet<>();
148

    
149
    @XmlElement(name = "Enabled")
150
    protected boolean enabled;
151

    
152
    @XmlElement(name = "AccountNonExpired")
153
    protected boolean accountNonExpired;
154

    
155
    @XmlElement(name = "CredentialsNonExpired")
156
    protected boolean credentialsNonExpired;
157

    
158
    @XmlElement(name = "AccountNonLocked")
159
    protected boolean accountNonLocked;
160

    
161
    @XmlElement(name = "Person")
162
    @XmlIDREF
163
    @XmlSchemaType(name = "IDREF")
164
    @OneToOne(fetch = FetchType.LAZY)
165
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
166
    @IndexedEmbedded(depth = 1)
167
    protected Person person;
168

    
169
    @XmlTransient
170
    @Transient
171
    private Set<GrantedAuthority> transientGrantedAuthorities;  //authorities of this user and of all groups the user belongs to
172

    
173
    @XmlElementWrapper(name = "Authorities")
174
    @XmlElement(name = "Authority", type = AuthorityBase.class)
175
    @XmlIDREF
176
    @XmlSchemaType(name = "IDREF")
177
    @ManyToMany(fetch = FetchType.LAZY, targetEntity = AuthorityBase.class)
178
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
179
    @NotAudited
180
    protected Set<AuthorityBase> authorities = new HashSet<>();
181

    
182
    @XmlTransient
183
    @Transient
184
    private Set<AuthorityBase> transientAuthorities;  //authorities of this user and of all groups the user belongs to
185

    
186
//***************************** Constructor *********************** /
187

    
188
    protected User(){
189
        super();
190
    }
191

    
192
// ***************************** GETTER / SETTER ******************************/
193

    
194
    public Person getPerson() {return person;}
195
    public void setPerson(Person person) {this.person = person;}
196

    
197
    @Override
198
    public String getPassword() {return password;}
199
    public void setPassword(String password) {this.password = password;}
200

    
201
    @Override
202
    public String getUsername() {return username;}
203
    public void setUsername(String username) {this.username = username;}
204

    
205
    @Override
206
    public boolean isAccountNonLocked() {return accountNonLocked;}
207
    public void setAccountNonLocked(boolean accountNonLocked) {this.accountNonLocked = accountNonLocked;}
208

    
209
    @Override
210
    public boolean isCredentialsNonExpired() {return credentialsNonExpired;}
211
    public void setCredentialsNonExpired(boolean credentialsNonExpired) {this.credentialsNonExpired = credentialsNonExpired;}
212

    
213
    public String getEmailAddress() {return emailAddress;}
214
    public void setEmailAddress(String emailAddress) {this.emailAddress = emailAddress;}
215

    
216
    @Override
217
    public boolean isEnabled() {return enabled;}
218
    public void setEnabled(boolean enabled) {this.enabled = enabled;}
219

    
220
    @Override
221
    public boolean isAccountNonExpired() {return accountNonExpired;}
222
    public void setAccountNonExpired(boolean accountNonExpired) {this.accountNonExpired = accountNonExpired;}
223

    
224
    protected void setGroups(Set<Group> groups) {
225
        this.groups = groups;
226
        initAuthorities();
227
    }
228
    public Set<Group> getGroups() {return groups;}
229

    
230
    public Set<GrantedAuthority> getGrantedAuthorities() {return grantedAuthorities;}
231
    public void setGrantedAuthorities(Set<GrantedAuthority> grantedAuthorities) {
232
        this.grantedAuthorities = grantedAuthorities;
233
        initAuthorities();
234
    }
235

    
236
    public Set<AuthorityBase> getAuthoritiesB() {return authorities;}
237
    public void setAuthorities(Set<AuthorityBase> authorities) {
238
        this.authorities = authorities;
239
        initAuthorities();
240
    }
241
    public void addAuthority(AuthorityBase authority){
242
        this.authorities.add(authority);
243
        initAuthorities();
244
    }
245

    
246
// ************************** METHODS *********************/
247

    
248
    /**
249
     * Initializes or refreshes the collection of authorities, See
250
     * {@link #getAuthorities()}
251
     */
252
    //FIXME made public as preliminary solution to #4053 (Transient field User.authorities not refreshed on reloading entity)
253
    public void initAuthorities() {
254
        //GrantedAuthority
255
        transientGrantedAuthorities = new HashSet<>();
256
        transientGrantedAuthorities.addAll(grantedAuthorities);
257
        for(Group group : groups) {
258
            transientGrantedAuthorities.addAll(group.getGrantedAuthorities());
259
        }
260
        //CdmAuthority
261
        //TODO activating this currently leads to LIE in AuthenticationPresenterTest in Vaadin project
262
//        transientAuthorities = new HashSet<>();
263
//        transientAuthorities.addAll(authorities);
264
//        for(Group group : groups) {
265
//            transientAuthorities.addAll(group.getAuthorities());
266
//        }
267
    }
268

    
269
    /**
270
     * Implementation of {@link UserDetails#getAuthorities()}
271
     *
272
     * {@inheritDoc}
273
     *
274
     * @return returns all {@code Set<GrantedAuthority>} instances contained in
275
     *         the sets {@link #getGrantedAuthorities()} and
276
     *         {@link #getGroups()}
277
     */
278
    @Override
279
    @Transient
280
    public Collection<GrantedAuthority> getAuthorities() {
281
        if(transientGrantedAuthorities == null || transientGrantedAuthorities.size() == 0) {
282
            initAuthorities();
283
        }
284
        return transientGrantedAuthorities;
285
    }
286

    
287
    public static User getCurrentAuthenticatedUser() {
288
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
289
        if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
290
            return (User)authentication.getPrincipal();
291
        }
292
        return null;
293
    }
294

    
295
//*********************** CLONE ********************************************************/
296

    
297
    /**
298
     * Clones <i>this</i> User. This is a shortcut that enables to create
299
     * a new instance that differs only slightly from <i>this</i> User.
300
     * The corresponding person is cloned.
301
     *
302
     * @see eu.etaxonomy.cdm.model.common.CdmBase#clone()
303
     * @see java.lang.Object#clone()
304
     */
305
    @Override
306
    public Object clone() {
307
        try{
308
            User result = (User)super.clone();
309
            if (this.person != null){
310
                result.setPerson((Person)this.person.clone());
311
            }
312
            return result;
313
        } catch (CloneNotSupportedException e){
314
            logger.warn("Object does not implement cloneable");
315
            e.printStackTrace();
316
            return null;
317
        }
318

    
319

    
320
    }
321

    
322
//************************************** toString ***************************************
323

    
324
    @Override
325
    public String toString() {
326
        if (StringUtils.isNotBlank(username)){
327
            return username;
328
        }else{
329
            return super.toString();
330
        }
331
    }
332
}
(9-9/9)