/** * Global Sensor Networks (GSN) Source Code * Copyright (c) 2006-2016, Ecole Polytechnique Federale de Lausanne (EPFL) * * This file is part of GSN. * * GSN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GSN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GSN. If not, see <http://www.gnu.org/licenses/>. * * File: app/models/gsn/auth/User.java * * @author Julien Eberle * */ package models.gsn.auth; import be.objectify.deadbolt.core.models.Permission; import be.objectify.deadbolt.core.models.Role; import be.objectify.deadbolt.core.models.Subject; import com.avaje.ebean.Ebean; import com.avaje.ebean.ExpressionList; import com.feth.play.module.pa.providers.password.UsernamePasswordAuthUser; import com.feth.play.module.pa.user.AuthUser; import com.feth.play.module.pa.user.AuthUserIdentity; import com.feth.play.module.pa.user.EmailIdentity; import com.feth.play.module.pa.user.NameIdentity; import com.feth.play.module.pa.user.FirstLastNameIdentity; import models.gsn.auth.TokenAction.Type; import play.data.format.Formats; import play.data.validation.Constraints; import javax.persistence.*; import java.util.*; /** * Initial version based on work by Steve Chaloner (steve@objectify.be) for * Deadbolt2 */ @Entity @Table(name = "users") public class User extends AppModel implements Subject { /** * */ private static final long serialVersionUID = 1L; @Id public Long id; @Constraints.Email // if you make this unique, keep in mind that users *must* merge/link their // accounts then on signup with additional providers @Column(unique = true) public String email; public String name; public String firstName; public String lastName; @Formats.DateTime(pattern = "yyyy-MM-dd HH:mm:ss") public Date lastLogin; public boolean active; public boolean emailValidated; @ManyToMany public List<SecurityRole> roles; @OneToMany(cascade = CascadeType.ALL) public List<LinkedAccount> linkedAccounts; @ManyToMany public List<UserPermission> permissions; @OneToMany(cascade = CascadeType.ALL) public List<UserDataSourceRead> dataSourceRead; @OneToMany(cascade = CascadeType.ALL) public List<UserDataSourceWrite> dataSourceWrite; @ManyToMany public List<Group> groups; @ManyToMany(mappedBy = "trusted_users") public List<Client> trusted_clients; public static play.db.ebean.Model.Finder<Long, User> find = new play.db.ebean.Model.Finder<Long, User>( Long.class, User.class); public static User findById(Long value) { return find.where().eq("id", value).findUnique(); } @Override public String getIdentifier() { return Long.toString(this.id); } @Override public List<? extends Role> getRoles() { return this.roles; } @Override public List<? extends Permission> getPermissions() { return this.permissions; } public boolean hasAccessTo(DataSource ds, Boolean toWrite){ if(toWrite && null != UserDataSourceWrite.findByBoth(this, ds)) return true; else if (!toWrite && null != UserDataSourceRead.findByBoth(this, ds)) return true; else { for (Group g : groups){ if(toWrite && null != GroupDataSourceWrite.findByBoth(g, ds)) return true; else if (!toWrite && null != GroupDataSourceRead.findByBoth(g, ds)) return true; } } return false; } public static boolean existsByAuthUserIdentity( final AuthUserIdentity identity) { final ExpressionList<User> exp; if (identity instanceof UsernamePasswordAuthUser) { exp = getUsernamePasswordAuthUserFind((UsernamePasswordAuthUser) identity); } else { exp = getAuthUserFind(identity); } return exp.findRowCount() > 0; } private static ExpressionList<User> getAuthUserFind( final AuthUserIdentity identity) { return find.where().eq("active", true) .eq("linkedAccounts.providerUserId", identity.getId()) .eq("linkedAccounts.providerKey", identity.getProvider()); } public static User findByAuthUserIdentity(final AuthUserIdentity identity) { if (identity == null) { return null; } if (identity instanceof UsernamePasswordAuthUser) { return findByUsernamePasswordIdentity((UsernamePasswordAuthUser) identity); } else { return getAuthUserFind(identity).findUnique(); } } public static User findByUsernamePasswordIdentity( final UsernamePasswordAuthUser identity) { return getUsernamePasswordAuthUserFind(identity).findUnique(); } private static ExpressionList<User> getUsernamePasswordAuthUserFind( final UsernamePasswordAuthUser identity) { return getEmailUserFind(identity.getEmail()).eq( "linkedAccounts.providerKey", identity.getProvider()); } public void merge(final User otherUser) { for (final LinkedAccount acc : otherUser.linkedAccounts) { this.linkedAccounts.add(LinkedAccount.create(acc)); } // do all other merging stuff here - like resources, etc. // deactivate the merged user that got added to this one otherUser.active = false; Ebean.save(Arrays.asList(new User[] { otherUser, this })); } public static User create(final AuthUser authUser) { final User user; if (authUser instanceof EmailIdentity) { User u = User.findByEmail(((EmailIdentity) authUser).getEmail()); if (u != null) { user = u; }else{ user = new User(); } }else{ user = new User(); } user.roles = Collections.singletonList(SecurityRole .findByRoleName(controllers.gsn.auth.LocalAuthController.USER_ROLE)); // user.permissions = new ArrayList<UserPermission>(); // user.permissions.add(UserPermission.findByValue("printers.edit")); user.active = true; user.lastLogin = new Date(); user.linkedAccounts = Collections.singletonList(LinkedAccount .create(authUser)); if (authUser instanceof EmailIdentity) { final EmailIdentity identity = (EmailIdentity) authUser; // Remember, even when getting them from FB & Co., emails should be // verified within the application as a security breach there might // break your security as well! user.email = identity.getEmail(); user.emailValidated = false; } if (authUser instanceof NameIdentity) { final NameIdentity identity = (NameIdentity) authUser; final String name = identity.getName(); if (name != null) { user.name = name; } } if (authUser instanceof FirstLastNameIdentity) { final FirstLastNameIdentity identity = (FirstLastNameIdentity) authUser; final String firstName = identity.getFirstName(); final String lastName = identity.getLastName(); if (firstName != null) { user.firstName = firstName; } if (lastName != null) { user.lastName = lastName; } } user.save(); // Ebean.saveManyToManyAssociations(user, "roles"); // Ebean.saveManyToManyAssociations(user, "permissions"); return user; } public static void merge(final AuthUser oldUser, final AuthUser newUser) { User.findByAuthUserIdentity(oldUser).merge( User.findByAuthUserIdentity(newUser)); } public Set<String> getProviders() { final Set<String> providerKeys = new HashSet<String>( this.linkedAccounts.size()); for (final LinkedAccount acc : this.linkedAccounts) { providerKeys.add(acc.providerKey); } return providerKeys; } public static void addLinkedAccount(final AuthUser oldUser, final AuthUser newUser) { final User u = User.findByAuthUserIdentity(oldUser); u.linkedAccounts.add(LinkedAccount.create(newUser)); u.save(); } public static void setLastLoginDate(final AuthUser knownUser) { final User u = User.findByAuthUserIdentity(knownUser); u.lastLogin = new Date(); u.save(); } public static User findByEmail(final String email) { return getEmailUserFind(email).findUnique(); } private static ExpressionList<User> getEmailUserFind(final String email) { return find.where().eq("active", true).eq("email", email); } public LinkedAccount getAccountByProvider(final String providerKey) { return LinkedAccount.findByProviderKey(this, providerKey); } public static void verify(final User unverified) { // You might want to wrap this into a transaction unverified.emailValidated = true; unverified.save(); TokenAction.deleteByUser(unverified, Type.EMAIL_VERIFICATION); } public void changePassword(final UsernamePasswordAuthUser authUser, final boolean create) { LinkedAccount a = this.getAccountByProvider(authUser.getProvider()); if (a == null) { if (create) { a = LinkedAccount.create(authUser); a.user = this; } else { throw new RuntimeException( "Account not enabled for password usage"); } } a.providerUserId = authUser.getHashedPassword(); a.save(); } public void resetPassword(final UsernamePasswordAuthUser authUser, final boolean create) { // You might want to wrap this into a transaction this.changePassword(authUser, create); TokenAction.deleteByUser(this, Type.PASSWORD_RESET); } }