commit c1016fab8887d0feb46e3aac2c8e24295e744520
Author: Petr Urban <urbanp@gapps.zcu.cz>
Date:   Tue Dec 19 22:58:46 2023 +0100

    SSO automatic registration

diff --git a/db/spawn/db.spawn_createUsersTable.sql b/db/spawn/db.spawn_createUsersTable.sql
index bcdcd96..611e10a 100644
--- a/db/spawn/db.spawn_createUsersTable.sql
+++ b/db/spawn/db.spawn_createUsersTable.sql
@@ -1,8 +1,8 @@
 if not exists (select * from sysobjects where name='users' and xtype='U')
 create table users(
     id int identity(1,1),
-    email nvarchar(255) not null,
+    email nvarchar(255) null,
     name nvarchar(255) not null,
-    password varchar(255) not null,
+    password varchar(255) null,
     PRIMARY KEY(id)
 );
\ No newline at end of file
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/AuthConfiguration.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/AuthConfiguration.java
index 281d568..015c850 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/AuthConfiguration.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/AuthConfiguration.java
@@ -3,6 +3,7 @@ package cz.zcu.fav.kiv.antipatterndetectionapp.v2.security;
 import com.nimbusds.jose.jwk.source.JWKSource;
 import com.nimbusds.jose.jwk.source.RemoteJWKSet;
 import com.nimbusds.jwt.proc.JWTProcessor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
@@ -41,6 +42,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.stereotype.Component;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.CorsConfigurationSource;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -54,7 +56,12 @@ import java.util.Arrays;
 import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
 
 @EnableWebSecurity
+@Component
 public class AuthConfiguration {
+
+    @Autowired
+    private JwtAuthenticationTokenConverter jwtAuthenticationTokenConverter;
+
     @Bean
     public SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http) throws Exception {
         http
@@ -65,7 +72,7 @@ public class AuthConfiguration {
                         .antMatchers("/v2/**").hasAnyRole("spade_basic")
                         .anyRequest().authenticated()
                 )
-                .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(new JwtAuthenticationTokenConverter())))
+                .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwtAuthenticationTokenConverter)))
                 .sessionManagement(session -> session.sessionCreationPolicy(STATELESS));
         return http.build();
     }
@@ -83,5 +90,4 @@ public class AuthConfiguration {
             }
         };
     }
-
 }
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/JwtAuthenticationTokenConverter.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/JwtAuthenticationTokenConverter.java
index e67ad49..eab9997 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/JwtAuthenticationTokenConverter.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/security/JwtAuthenticationTokenConverter.java
@@ -1,5 +1,10 @@
 package cz.zcu.fav.kiv.antipatterndetectionapp.v2.security;
 
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.model.User;
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.service.user.UserService;
+import cz.zcu.fav.kiv.antipatterndetectionapp.v2.service.user.UserServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.core.GrantedAuthority;
@@ -8,6 +13,7 @@ import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtClaimNames;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
+import org.springframework.stereotype.Service;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -15,9 +21,11 @@ import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+@Service
 public class JwtAuthenticationTokenConverter implements Converter<Jwt, AbstractAuthenticationToken> {
-
     private static final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
+    @Autowired
+    private UserService userService;
     private String resourceId = "spade-client";
     private String principalAttribute = "preferred_username";
 
@@ -27,6 +35,10 @@ public class JwtAuthenticationTokenConverter implements Converter<Jwt, AbstractA
                 Stream.concat(jwtGrantedAuthoritiesConverter.convert(jwt).stream(), extractResourceRoles(jwt).stream())
                         .collect(Collectors.toSet());
         String claimName = principalAttribute == null ? JwtClaimNames.SUB : principalAttribute;
+
+        String preferredUsername = jwt.getClaim(claimName);
+        userService.registerUserSSO(new User(preferredUsername));
+
         return new JwtAuthenticationToken(jwt, authorities, jwt.getClaim(claimName));
     }
 
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserService.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserService.java
index f8f2f85..58e74dc 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserService.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserService.java
@@ -15,6 +15,9 @@ public interface UserService {
      * @return Integer - status code of the operation, ie 1 - successful, 0 - name taken, ...
      */
     public UserModelStatusCodes registerUser(User user);
+
+    void registerUserSSO(User user);
+
     /**
      * Method attempts to log in a user and returns status code indicating login result
      * @param user serialized JSON object representing user
diff --git a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserServiceImpl.java b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserServiceImpl.java
index 857eb9f..39a0f1b 100644
--- a/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserServiceImpl.java
+++ b/src/main/java/cz/zcu/fav/kiv/antipatterndetectionapp/v2/service/user/UserServiceImpl.java
@@ -5,6 +5,7 @@ import cz.zcu.fav.kiv.antipatterndetectionapp.v2.model.User;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.repository.UserRepository;
 import cz.zcu.fav.kiv.antipatterndetectionapp.v2.utils.Crypto;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
 import org.springframework.stereotype.Service;
 import java.util.regex.Pattern;
 
@@ -18,6 +19,18 @@ public class UserServiceImpl implements UserService {
     @Autowired
     private UserRepository userRepository;
 
+
+    @Override
+    public synchronized void registerUserSSO(User user) {
+        if (userRepository.findByName(user.getName()) == null) {
+            final String name = user.getName();
+            if (verifyUserParameters(name))
+                return;
+            //save the user
+            userRepository.save(new User(name));
+        }
+    }
+
     /**
      * Method attempts to register a user
      * @param user serialized JSON object representing user
@@ -107,7 +120,9 @@ public class UserServiceImpl implements UserService {
         return (!passwordMatches ? UserModelStatusCodes.USER_LOGIN_FAILED : UserModelStatusCodes.USER_LOGGED_IN);
     }
 
-
+    private boolean verifyUserParameters(String name) {
+        return (name == null || name.isEmpty() || name.isBlank());
+    }
 
     @Override
     public User getUserByName(String name) {
