commit deb64a56eb71ff8781bbf331c30858618b22b9d4
Author: Jakub Danek <jakub.danek@yoso.fi>
Date:   Tue Oct 15 15:27:42 2019 +0200

    re #29 backend implementation of Google OAuth2 authentication
    
    Includes new user registration.

diff --git a/server/init.sql b/server/init.sql
index 51d4fb5..8223bd8 100644
--- a/server/init.sql
+++ b/server/init.sql
@@ -12,7 +12,6 @@ CREATE TABLE end_user (
   no_sick_days INT,
   taken_sick_days INT NOT NULL,
   alert DATETIME,
-  token TEXT NOT NULL,
   email VARCHAR(100) NOT NULL,
   photo TEXT,
   creation_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -116,9 +115,9 @@ DELIMITER ;
 -- -----------------------------------------------------
 -- Insert table end_user
 -- -----------------------------------------------------
-INSERT INTO end_user (first_name, last_name, no_vacations, no_sick_days, taken_sick_days, alert, token, email, photo,
+INSERT INTO end_user (first_name, last_name, no_vacations, no_sick_days, taken_sick_days, alert, email, photo,
                       user_role, status)
-VALUES ('admin', 'admin', 0, NULL, 0, NULL, '', 'testuser@yoso.fi',
+VALUES ('admin', 'admin', 0, NULL, 0, NULL, 'testuser@yoso.fi',
         'https://st2.depositphotos.com/9223672/12056/v/950/depositphotos_120568236-stock-illustration-male-face-avatar-logo-template.jpg',
         'EMPLOYER', 'ACCEPTED');
 
diff --git a/server/pom.xml b/server/pom.xml
index be772bc..6ecb946 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -29,6 +29,15 @@
             <artifactId>spring-boot-starter-security</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-oauth2-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-oauth2-jose</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
diff --git a/server/src/main/java/org/danekja/ymanager/WebSecurityConfiguration.java b/server/src/main/java/org/danekja/ymanager/WebSecurityConfiguration.java
index d54d483..310949d 100644
--- a/server/src/main/java/org/danekja/ymanager/WebSecurityConfiguration.java
+++ b/server/src/main/java/org/danekja/ymanager/WebSecurityConfiguration.java
@@ -1,7 +1,10 @@
 package org.danekja.ymanager;
 
+import org.danekja.ymanager.business.UserManager;
+import org.danekja.ymanager.business.auth.service.GoogleOidcUserService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.authentication.AuthenticationProvider;
@@ -13,11 +16,17 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
+import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
 
 @Configuration
 @EnableWebSecurity
 public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
 
+    @Autowired
+    private OAuth2UserService<OidcUserRequest, OidcUser> googleOauthUserService;
+
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.cors()
@@ -26,7 +35,13 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
                 .authorizeRequests()
                 .anyRequest().authenticated()
                 .and()
-                .httpBasic();
+                .oauth2Login()
+                .userInfoEndpoint().oidcUserService(googleOauthUserService);
+    }
+
+    @Bean
+    public OAuth2UserService<OidcUserRequest, OidcUser> googleOauthUserService(UserManager userManager) {
+        return new GoogleOidcUserService(userManager);
     }
 
     /**
diff --git a/server/src/main/java/org/danekja/ymanager/business/UserManager.java b/server/src/main/java/org/danekja/ymanager/business/UserManager.java
index 52be451..29ea923 100644
--- a/server/src/main/java/org/danekja/ymanager/business/UserManager.java
+++ b/server/src/main/java/org/danekja/ymanager/business/UserManager.java
@@ -1,9 +1,11 @@
 package org.danekja.ymanager.business;
 
+import org.danekja.ymanager.domain.GoogleUser;
 import org.danekja.ymanager.domain.Status;
 import org.danekja.ymanager.domain.User;
 import org.danekja.ymanager.dto.BasicProfileUser;
 import org.danekja.ymanager.ws.rest.exceptions.RESTFullException;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
 
 import java.util.List;
 
@@ -30,4 +32,8 @@ public interface UserManager {
      * @return found user Object or null
      */
     User getUser(Long userId);
+
+    GoogleUser registerUser(OidcIdToken user);
+
+    GoogleUser getUser(OidcIdToken token);
 }
diff --git a/server/src/main/java/org/danekja/ymanager/business/auth/service/GoogleOidcUserService.java b/server/src/main/java/org/danekja/ymanager/business/auth/service/GoogleOidcUserService.java
new file mode 100644
index 0000000..8e2fcc0
--- /dev/null
+++ b/server/src/main/java/org/danekja/ymanager/business/auth/service/GoogleOidcUserService.java
@@ -0,0 +1,57 @@
+package org.danekja.ymanager.business.auth.service;
+
+import org.danekja.ymanager.business.UserManager;
+import org.danekja.ymanager.domain.GoogleUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
+import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
+import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * Customer {@link OAuth2UserService} of OpenID protocol, which populates user
+ * object from data stored in database.
+ * <p>
+ * If the authenticated user does not have record in database, it is created.
+ */
+@Transactional
+@Service
+public class GoogleOidcUserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
+
+    private final UserManager userManager;
+
+    @Value("#{'${ymanager.oauth2.client.google.allowed-domains}'.split(',')}")
+    private List<String> allowedDomains;
+
+    @Autowired
+    public GoogleOidcUserService(UserManager userManager) {
+        this.userManager = userManager;
+    }
+
+    @Override
+    public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
+        //this ensures only users from configured domains can log-in
+        //also, Google allows to configure application as "internal", after that only accounts from your domain
+        //can sign-in
+        if (!allowedDomains.isEmpty() && !allowedDomains.contains(userRequest.getIdToken().getClaimAsString("hd"))) {
+            throw new OAuth2AuthenticationException(new OAuth2Error("403"), "Unauthorized!");
+        }
+
+        OidcIdToken token = userRequest.getIdToken();
+
+        GoogleUser user = userManager.getUser(userRequest.getIdToken());
+
+        if (user == null) {
+            user = userManager.registerUser(token);
+        }
+
+        return user;
+    }
+}
diff --git a/server/src/main/java/org/danekja/ymanager/business/impl/DefaultUserManager.java b/server/src/main/java/org/danekja/ymanager/business/impl/DefaultUserManager.java
index a6eb5e7..f12d47c 100644
--- a/server/src/main/java/org/danekja/ymanager/business/impl/DefaultUserManager.java
+++ b/server/src/main/java/org/danekja/ymanager/business/impl/DefaultUserManager.java
@@ -3,9 +3,9 @@ package org.danekja.ymanager.business.impl;
 import org.danekja.ymanager.business.UserManager;
 import org.danekja.ymanager.business.auth.anot.IsEmployer;
 import org.danekja.ymanager.business.auth.anot.IsOwner;
-import org.danekja.ymanager.domain.Status;
-import org.danekja.ymanager.domain.User;
+import org.danekja.ymanager.domain.*;
 import org.danekja.ymanager.dto.BasicProfileUser;
+import org.danekja.ymanager.repository.SettingsRepository;
 import org.danekja.ymanager.repository.UserRepository;
 import org.danekja.ymanager.repository.VacationRepository;
 import org.danekja.ymanager.ws.rest.exceptions.RESTFullException;
@@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDate;
@@ -31,11 +32,13 @@ public class DefaultUserManager implements UserManager {
 
     private final UserRepository userRepository;
     private final VacationRepository vacationRepository;
+    private final SettingsRepository settingsRepository;
 
     @Autowired
-    public DefaultUserManager(UserRepository userRepository, VacationRepository vacationRepository) {
+    public DefaultUserManager(UserRepository userRepository, VacationRepository vacationRepository, SettingsRepository settingsRepository) {
         this.userRepository = userRepository;
         this.vacationRepository = vacationRepository;
+        this.settingsRepository = settingsRepository;
     }
 
     @Override
@@ -44,6 +47,15 @@ public class DefaultUserManager implements UserManager {
         return userRepository.getUser(userId);
     }
 
+    @Override
+    public GoogleUser getUser(OidcIdToken token) {
+        //TODO replace with better user object creation after we update the data model - registered and oauth users should be
+        //TODO in separate tables. We do this here because we need the ID value set
+        User user = userRepository.getUser(token.getEmail());
+
+        return user != null ? new GoogleUser(user.getId(), user.getUserData(), token) : null;
+    }
+
     @Override
     @IsEmployer
     public List<BasicProfileUser> getUsers(Status status) throws RESTFullException {
@@ -65,4 +77,17 @@ public class DefaultUserManager implements UserManager {
         }
     }
 
+    @Override
+    public GoogleUser registerUser(OidcIdToken token) {
+        DefaultSettings ds = settingsRepository.get();
+        //TODO state is Accepted until #45 is resolved
+        GoogleUser user = new GoogleUser(null, new UserData(ds.getSickDayCount(), ds.getNotification(), UserRole.EMPLOYEE, Status.ACCEPTED), token);
+
+        long id = userRepository.insertUser(user);
+        UserData data = userRepository.getUserData(id);
+
+        return new GoogleUser(id, data, user.getIdToken());
+    }
+
+
 }
diff --git a/server/src/main/java/org/danekja/ymanager/domain/GoogleUser.java b/server/src/main/java/org/danekja/ymanager/domain/GoogleUser.java
new file mode 100644
index 0000000..523a4dc
--- /dev/null
+++ b/server/src/main/java/org/danekja/ymanager/domain/GoogleUser.java
@@ -0,0 +1,89 @@
+package org.danekja.ymanager.domain;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
+import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Represents user authenticated via Google OAuth2 provider
+ */
+public class GoogleUser extends User implements OidcUser {
+
+    private static final String HOSTED_DOMAIN = "hd";
+
+    private final OidcIdToken idToken;
+
+    public GoogleUser(Long id, UserData userData, OidcIdToken idToken) {
+        super(id, userData);
+        this.idToken = idToken;
+    }
+
+    public GoogleUser(OidcIdToken idToken) {
+        this.idToken = idToken;
+    }
+
+    public String subjectId() {
+        return idToken.getSubject();
+    }
+
+    @Override
+    public String getFirstName() {
+        return idToken.getGivenName();
+    }
+
+    @Override
+    public String getLastName() {
+        return idToken.getFamilyName();
+    }
+
+    @Override
+    public String getEmail() {
+        return idToken.getEmail();
+    }
+
+    @Override
+    public String getPicture() {
+        return idToken.getPicture();
+    }
+
+    public String getDomain() {
+        return idToken.getClaimAsString(HOSTED_DOMAIN);
+    }
+
+    @Override
+    public Map<String, Object> getClaims() {
+        return idToken.getClaims();
+    }
+
+    @Override
+    public OidcUserInfo getUserInfo() {
+        return null;
+    }
+
+    @Override
+    public OidcIdToken getIdToken() {
+        return idToken;
+    }
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        //TODO do not create collection on each call
+        return Collections.singleton(new SimpleGrantedAuthority(userData.getRole().name()));
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return idToken.getClaims();
+    }
+
+    @Override
+    public String getName() {
+        return idToken.getFullName();
+    }
+}
diff --git a/server/src/main/java/org/danekja/ymanager/domain/User.java b/server/src/main/java/org/danekja/ymanager/domain/User.java
index df81bbc..bd001b6 100644
--- a/server/src/main/java/org/danekja/ymanager/domain/User.java
+++ b/server/src/main/java/org/danekja/ymanager/domain/User.java
@@ -74,6 +74,13 @@ public abstract class User {
      */
     public abstract String getLastName();
 
+    /**
+     * @return Returns full object with user's data
+     */
+    public UserData getUserData() {
+        return userData;
+    }
+
     /**
      * Returns the number of user's remaining hours of an overtime.
      *
diff --git a/server/src/main/java/org/danekja/ymanager/domain/UserData.java b/server/src/main/java/org/danekja/ymanager/domain/UserData.java
index 2d7401e..0c14093 100644
--- a/server/src/main/java/org/danekja/ymanager/domain/UserData.java
+++ b/server/src/main/java/org/danekja/ymanager/domain/UserData.java
@@ -8,31 +8,38 @@ public class UserData {
     /**
      * The number of user's remaining hours of an overtime.
      */
-    private Float vacationCount;
+    private Float vacationCount = 0f;
     /**
      * The number of user's sick days available during a year.
      */
-    private Integer totalSickDayCount;
+    private Integer totalSickDayCount = 0;
     /**
      * The number of user's taken sick days.
      */
-    private Integer takenSickDayCount;
+    private Integer takenSickDayCount = 0;
     /**
      * The date and time of sending an email warning about an incoming reset of remaining overtimes and sick days.
      */
-    private LocalDateTime notification;
+    private LocalDateTime notification = null;
     /**
      * The user's role.
      */
-    private UserRole role;
+    private UserRole role = null;
     /**
      * The user's authorization status.
      */
-    private Status status;
+    private Status status = null;
 
     public UserData() {
     }
 
+    public UserData(Integer totalSickDayCount, LocalDateTime notification, UserRole role, Status status) {
+        this.totalSickDayCount = totalSickDayCount;
+        this.notification = notification;
+        this.role = role;
+        this.status = status;
+    }
+
     public UserData(Float vacationCount, Integer totalSickDayCount, Integer takenSickDayCount, LocalDateTime notification, UserRole role, Status status) {
         this.vacationCount = vacationCount;
         this.totalSickDayCount = totalSickDayCount;
diff --git a/server/src/main/java/org/danekja/ymanager/repository/UserRepository.java b/server/src/main/java/org/danekja/ymanager/repository/UserRepository.java
index 4f55441..d5d1337 100644
--- a/server/src/main/java/org/danekja/ymanager/repository/UserRepository.java
+++ b/server/src/main/java/org/danekja/ymanager/repository/UserRepository.java
@@ -1,27 +1,22 @@
 package org.danekja.ymanager.repository;
 
-import org.danekja.ymanager.domain.RegisteredUser;
-import org.danekja.ymanager.domain.Status;
-import org.danekja.ymanager.domain.User;
-import org.danekja.ymanager.domain.UserRole;
+import org.danekja.ymanager.domain.*;
 import org.danekja.ymanager.dto.BasicProfileUser;
-import org.danekja.ymanager.dto.DefaultSettings;
 import org.danekja.ymanager.dto.FullUserProfile;
+import org.danekja.ymanager.repository.jdbc.mappers.UserDataRowMapper;
 import org.danekja.ymanager.repository.jdbc.mappers.UserRowMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.RowMapper;
-import org.springframework.jdbc.core.SqlOutParameter;
-import org.springframework.jdbc.core.SqlParameter;
+import org.springframework.jdbc.core.*;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
 import org.springframework.stereotype.Repository;
 
 import java.sql.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
+import java.util.Objects;
 
 import static org.danekja.ymanager.domain.UserRole.getUserRole;
 
@@ -31,7 +26,9 @@ import static org.danekja.ymanager.domain.UserRole.getUserRole;
 @Repository
 public class UserRepository {
 
-    private final RowMapper<RegisteredUser> USER_MAPPER = new UserRowMapper();
+    private final RowMapper<UserData> USER_DATA_MAPPER = new UserDataRowMapper();
+    private final RowMapper<RegisteredUser> USER_MAPPER = new UserRowMapper(USER_DATA_MAPPER);
+
 
     /**
      * The mapper maps a row from a result of a query to an BasicProfileUser.
@@ -56,27 +53,6 @@ public class UserRepository {
         }
     }
 
-    /**
-     * The mapper maps a row from a result of a query to an BasicProfileUser.
-     */
-    private class DefaultSettingsMapper implements RowMapper<DefaultSettings> {
-
-        /**
-         * Maps a row from a result of a query to an DefaultSettings.
-         * @param resultSet the row from the result
-         * @param i the index of the row
-         * @return the DefaultSettings object
-         * @throws SQLException if the columnLabel is not valid; if a database access error occurs or this method is called on a closed result set
-         */
-        @Override
-        public DefaultSettings mapRow(ResultSet resultSet, int i) throws SQLException {
-            final DefaultSettings settings = new DefaultSettings();
-            settings.setSickDayCount(resultSet.getInt("no_sick_days"));
-            settings.setNotification(resultSet.getTimestamp("alert").toLocalDateTime());
-            return settings;
-        }
-    }
-
     /**
      * The logger.
      */
@@ -200,9 +176,6 @@ public class UserRepository {
     }
 
 
-    public Optional<DefaultSettings> getLastDefaultSettings() {
-        return Optional.ofNullable(this.jdbc.queryForObject("SELECT * FROM default_settings ORDER BY id DESC LIMIT 1", new DefaultSettingsMapper()));
-    }
 
     //---------------------------------- DOMAIN -----------------------------------
 
@@ -230,14 +203,45 @@ public class UserRepository {
         return RepositoryUtils.singleResult(users);
     }
 
+    /**
+     * Gets user data by user's id
+     *
+     * @param id TODO replace by subject Id for google and numeric id for registered (multiple queries)
+     * @return
+     */
+    public UserData getUserData(final long id) {
+        List<UserData> users = this.jdbc.query("SELECT * FROM end_user WHERE id = ?", USER_DATA_MAPPER, id);
+
+        return RepositoryUtils.singleResult(users);
+    }
+
     public void updateUser(final User user) {
         this.jdbc.update("UPDATE end_user SET first_name = ?, last_name = ?, no_vacations = ?, taken_sick_days = ?, email = ?, photo = ?, user_role = ?, status = ? WHERE id = ?",
                 user.getFirstName(), user.getLastName(), user.getVacationCount(), user.getTakenSickDayCount(), user.getEmail(), user.getPhoto(), user.getRole().name(), user.getStatus().name(), user.getId());
     }
 
-    public void insertUser(final User user) {
-        this.jdbc.update("INSERT INTO end_user (first_name, last_name, no_vacations, no_sick_days, taken_sick_days, alert, email, photo, user_role, status) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
-                user.getFirstName(), user.getLastName(), user.getVacationCount(), user.getTotalSickDayCount(), user.getTakenSickDayCount(), user.getNotification(), user.getEmail(), user.getPhoto(), user.getRole().name(), user.getStatus().name());
+    public long insertUser(final User user) {
+        PreparedStatementCreatorFactory factory = new PreparedStatementCreatorFactory("INSERT INTO end_user (first_name, last_name, no_vacations, no_sick_days, taken_sick_days, alert, email, photo, user_role, status) VALUES (?,?,?,?,?,?,?,?,?,?)",
+                Types.VARCHAR,
+                Types.VARCHAR,
+                Types.FLOAT,
+                Types.INTEGER,
+                Types.INTEGER,
+                Types.TIMESTAMP,
+                Types.VARCHAR,
+                Types.VARCHAR,
+                Types.VARCHAR,
+                Types.VARCHAR);
+
+
+        factory.setReturnGeneratedKeys(true);
+
+        PreparedStatementCreator stmt = factory.newPreparedStatementCreator(new Object[]{user.getFirstName(), user.getLastName(), user.getVacationCount(), user.getTotalSickDayCount(), user.getTakenSickDayCount(), user.getNotification(), user.getEmail(), user.getPhoto(), user.getRole().name(), user.getStatus().name()});
+
+        GeneratedKeyHolder key = new GeneratedKeyHolder();
+        this.jdbc.update(stmt, key);
+
+        return Objects.requireNonNull(key.getKey()).longValue();
     }
 
     public void insertSettings(final DefaultSettings settings) {
diff --git a/server/src/main/java/org/danekja/ymanager/repository/jdbc/mappers/UserDataRowMapper.java b/server/src/main/java/org/danekja/ymanager/repository/jdbc/mappers/UserDataRowMapper.java
new file mode 100644
index 0000000..e94fdf5
--- /dev/null
+++ b/server/src/main/java/org/danekja/ymanager/repository/jdbc/mappers/UserDataRowMapper.java
@@ -0,0 +1,30 @@
+package org.danekja.ymanager.repository.jdbc.mappers;
+
+import org.danekja.ymanager.domain.Status;
+import org.danekja.ymanager.domain.UserData;
+import org.danekja.ymanager.domain.UserRole;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+
+public class UserDataRowMapper implements RowMapper<UserData> {
+
+    @Override
+    public UserData mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+        float vacations = resultSet.getFloat("no_vacations");
+        int sickDays = resultSet.getInt("no_sick_days");
+        int takenSickDays = resultSet.getInt("taken_sick_days");
+
+        Timestamp date = resultSet.getTimestamp("alert");
+        LocalDateTime alert = date != null ? ((Timestamp) date).toLocalDateTime() : null;
+
+
+        UserRole role = UserRole.valueOf(resultSet.getString("user_role"));
+        Status status = Status.valueOf(resultSet.getString("status"));
+
+        return new UserData(vacations, sickDays, takenSickDays, alert, role, status);
+    }
+}
diff --git a/server/src/main/java/org/danekja/ymanager/repository/jdbc/mappers/UserRowMapper.java b/server/src/main/java/org/danekja/ymanager/repository/jdbc/mappers/UserRowMapper.java
index 28934e4..6b9fdb3 100644
--- a/server/src/main/java/org/danekja/ymanager/repository/jdbc/mappers/UserRowMapper.java
+++ b/server/src/main/java/org/danekja/ymanager/repository/jdbc/mappers/UserRowMapper.java
@@ -1,9 +1,7 @@
 package org.danekja.ymanager.repository.jdbc.mappers;
 
 import org.danekja.ymanager.domain.RegisteredUser;
-import org.danekja.ymanager.domain.Status;
 import org.danekja.ymanager.domain.UserData;
-import org.danekja.ymanager.domain.UserRole;
 import org.springframework.jdbc.core.RowMapper;
 
 import java.sql.ResultSet;
@@ -16,27 +14,25 @@ import java.time.LocalDateTime;
  */
 public class UserRowMapper implements RowMapper<RegisteredUser> {
 
+    private final RowMapper<UserData> dataRowMapper;
+
+    public UserRowMapper(RowMapper<UserData> dataRowMapper) {
+        this.dataRowMapper = dataRowMapper;
+    }
+
     @Override
     public RegisteredUser mapRow(ResultSet resultSet, int i) throws SQLException {
         Long id = resultSet.getLong("id");
         String firstName = resultSet.getString("first_name");
         String lastName = resultSet.getString("last_name");
-        float vacations = resultSet.getFloat("no_vacations");
-        int sickDays = resultSet.getInt("no_sick_days");
-        int takenSickDays = resultSet.getInt("taken_sick_days");
-
-        Timestamp date = resultSet.getTimestamp("alert");
-        LocalDateTime alert = date != null ? ((Timestamp) date).toLocalDateTime() : null;
 
         String email = resultSet.getString("email");
         String photo = resultSet.getNString("photo");
 
-        date = resultSet.getTimestamp("creation_date");
+        Timestamp date = resultSet.getTimestamp("creation_date");
         LocalDateTime creationDate = date.toLocalDateTime();
-        UserRole role = UserRole.valueOf(resultSet.getString("user_role"));
-        Status status = Status.valueOf(resultSet.getString("status"));
 
-        UserData userData = new UserData(vacations, sickDays, takenSickDays, alert, role, status);
+        UserData userData = dataRowMapper.mapRow(resultSet, i);
         return new RegisteredUser(id, firstName, lastName, email, photo, creationDate, userData);
     }
 }
diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties
index f4fd775..0b65394 100644
--- a/server/src/main/resources/application.properties
+++ b/server/src/main/resources/application.properties
@@ -2,4 +2,10 @@ server.servlet.context-path=/api
 spring.datasource.url=jdbc:mariadb://db:3306/yamanager
 spring.datasource.username=user
 spring.datasource.password=passwd
-spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
\ No newline at end of file
+spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
+#populate these from your google oauth2 setup
+#https://www.baeldung.com/spring-security-5-oauth2-login
+#spring.security.oauth2.client.registration.google.client-id=<client id value here>
+#spring.security.oauth2.client.registration.google.client-secret=<secret here>
+#comma-separated list of allowed domains, leave empty for all
+#ymanager.oauth2.client.google.allowed-domains=domain.com,domain2.com
\ No newline at end of file
