diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..25e28f0 Binary files /dev/null and b/.DS_Store differ diff --git a/EbfEmployees/pom.xml b/EbfEmployees/pom.xml index 3e27f09..2167219 100644 --- a/EbfEmployees/pom.xml +++ b/EbfEmployees/pom.xml @@ -34,10 +34,10 @@ org.springframework.boot spring-boot-starter-web - - org.flywaydb - flyway-core - + + + + com.h2database h2 @@ -106,7 +106,7 @@ 3 true - -Xmx1024m -XX:MaxPermSize=256m + -Xmx1024m diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/EbfEmployeesApplication.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/EbfEmployeesApplication.java index 0d8fc1a..d1b5e47 100644 --- a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/EbfEmployeesApplication.java +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/EbfEmployeesApplication.java @@ -1,7 +1,9 @@ package com.itekako.EbfEmployees; import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.retry.annotation.EnableRetry; import org.springframework.transaction.annotation.EnableTransactionManagement; diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/DatabaseFilter.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/DatabaseFilter.java new file mode 100644 index 0000000..69f285f --- /dev/null +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/DatabaseFilter.java @@ -0,0 +1,27 @@ +package com.itekako.EbfEmployees.auth; + +import org.springframework.core.annotation.Order; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Order(10000) +public class DatabaseFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String database = request.getHeader("Selected-Database"); + if(database == null){ + filterChain.doFilter (request,response); + return; + } + TenantAuthentificationToken authentication = (TenantAuthentificationToken) SecurityContextHolder.getContext().getAuthentication(); + authentication.setDatabase(database); + filterChain.doFilter (request,response); + } +} diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/JwtFilter.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/JwtFilter.java index b8f7f7f..f3b5a12 100644 --- a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/JwtFilter.java +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/JwtFilter.java @@ -63,7 +63,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse roles.add(new SimpleGrantedAuthority("ROLE_" + role.asString())); } SecurityContextHolder.getContext().setAuthentication( - new UsernamePasswordAuthenticationToken(subject,null, roles)); + new TenantAuthentificationToken(subject, roles)); super.doFilterInternal(request, response, chain); } } diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/TenantAuthentificationToken.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/TenantAuthentificationToken.java new file mode 100644 index 0000000..041afbe --- /dev/null +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/auth/TenantAuthentificationToken.java @@ -0,0 +1,27 @@ +package com.itekako.EbfEmployees.auth; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class TenantAuthentificationToken extends UsernamePasswordAuthenticationToken { + + private final String userId; + private String database; + + public TenantAuthentificationToken(String userId,Collection authorities) { + super(userId,null,authorities); + this.userId = userId; + } + + + public String getDatabase(){ + return database; + } + + public void setDatabase(String database){ + this.database = database; + } +} diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/configurations/SessionConfiguration.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/configurations/SessionConfiguration.java new file mode 100644 index 0000000..d168f5a --- /dev/null +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/configurations/SessionConfiguration.java @@ -0,0 +1,11 @@ +package com.itekako.EbfEmployees.configurations; + +import org.hibernate.SessionFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SessionConfiguration { + + +} diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/configurations/WebSecurityConfiguration.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/configurations/WebSecurityConfiguration.java index 69a75de..e129ae4 100644 --- a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/configurations/WebSecurityConfiguration.java +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/configurations/WebSecurityConfiguration.java @@ -1,5 +1,6 @@ package com.itekako.EbfEmployees.configurations; +import com.itekako.EbfEmployees.auth.DatabaseFilter; import com.itekako.EbfEmployees.auth.JwtFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -23,9 +24,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/swagger-ui.html", "/api-docs/**", "/webjars/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll() //allow swagger - .antMatchers("/api/**").hasRole("admin").anyRequest().authenticated() + .antMatchers("/api/**").authenticated() .and().csrf().disable().cors() - .and().addFilter(new JwtFilter(authenticationManager(), authConfiguration)); + .and().addFilter(new JwtFilter(authenticationManager(), authConfiguration)).addFilterAfter(new DatabaseFilter(),JwtFilter.class); } @Bean diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/controllers/CompanyController.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/controllers/CompanyController.java index 1ed81e8..37c789d 100644 --- a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/controllers/CompanyController.java +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/controllers/CompanyController.java @@ -83,4 +83,5 @@ public ResponseEntity generateEmployees(@PathVariable Long id) throws ResourceNo employeeService.generateEmployees(id); return ResponseEntity.noContent().build(); } + } diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/database/CurrentTenantIdentifierResolverImpl.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/database/CurrentTenantIdentifierResolverImpl.java new file mode 100644 index 0000000..96d1145 --- /dev/null +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/database/CurrentTenantIdentifierResolverImpl.java @@ -0,0 +1,37 @@ +package com.itekako.EbfEmployees.database; + +import com.itekako.EbfEmployees.auth.TenantAuthentificationToken; +import org.hibernate.context.spi.CurrentTenantIdentifierResolver; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) +public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver { + + + @Override + public String resolveCurrentTenantIdentifier() { + SecurityContext context = SecurityContextHolder.getContext(); + if(context.getAuthentication() == null)return "admin"; + Authentication authentication = context.getAuthentication(); + if(authentication instanceof AnonymousAuthenticationToken){ + return "admin"; + } + return ((TenantAuthentificationToken) authentication).getDatabase(); + } + + @Override + public boolean validateExistingCurrentSessions() { + return true; + } +} diff --git a/EbfEmployees/src/main/java/com/itekako/EbfEmployees/database/MapMultiTenantConnectionProvider.java b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/database/MapMultiTenantConnectionProvider.java new file mode 100644 index 0000000..9633242 --- /dev/null +++ b/EbfEmployees/src/main/java/com/itekako/EbfEmployees/database/MapMultiTenantConnectionProvider.java @@ -0,0 +1,46 @@ +package com.itekako.EbfEmployees.database; + +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +@Component +public class MapMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider { + + private Map connectionProviderMap + = new HashMap<>(); + + public MapMultiTenantConnectionProvider() throws IOException { + initConnectionProviderForTenant("user"); + initConnectionProviderForTenant("admin"); + } + + @Override + protected ConnectionProvider getAnyConnectionProvider() { + return connectionProviderMap.values() + .iterator() + .next(); + } + + @Override + protected ConnectionProvider selectConnectionProvider(String s) { + return connectionProviderMap.get(s); + } + + private void initConnectionProviderForTenant(String tenantId) + throws IOException { + Properties properties = new Properties(); + properties.load(getClass().getResourceAsStream( + String.format("/hibernate-database-%s.properties", tenantId))); + DriverManagerConnectionProviderImpl connectionProvider + = new DriverManagerConnectionProviderImpl(); + connectionProvider.configure(properties); + this.connectionProviderMap.put(tenantId, connectionProvider); + } +} diff --git a/EbfEmployees/src/main/resources/application.properties b/EbfEmployees/src/main/resources/application.properties index c77274b..32ffd8f 100644 --- a/EbfEmployees/src/main/resources/application.properties +++ b/EbfEmployees/src/main/resources/application.properties @@ -1,14 +1,7 @@ logging.level.root=debug -spring.datasource.initialization-mode=always -spring.datasource.platform=postgres -spring.datasource.url=jdbc:postgresql://localhost:5432/ebf_employees -spring.datasource.username=ebf_db_user -spring.datasource.password=ebf_db_password - -flyway.user=ebf_db_user -flyway.password=ebf_db_password -flyway.schemas=public -flyway.url=jdbc:postgresql://localhost:5432/ebf_employees +spring.jpa.properties.hibernate.multiTenancy=DATABASE +spring.jpa.properties.hibernate.tenant_identifier_resolver=com.itekako.EbfEmployees.database.CurrentTenantIdentifierResolverImpl +spring.jpa.properties.hibernate.multi_tenant_connection_provider=com.itekako.EbfEmployees.database.MapMultiTenantConnectionProvider spring.mvc.pathmatch.matching-strategy=ant_path_matcher log4j.logger.org.springframework.transaction=INFO diff --git a/EbfEmployees/src/main/resources/hibernate-database-admin.properties b/EbfEmployees/src/main/resources/hibernate-database-admin.properties new file mode 100644 index 0000000..37f9801 --- /dev/null +++ b/EbfEmployees/src/main/resources/hibernate-database-admin.properties @@ -0,0 +1,4 @@ +hibernate.connection.driver_class=org.postgresql.Driver +hibernate.connection.url=jdbc:postgresql://localhost:5432/ebf_employees2 +hibernate.connection.username=ebf_db_user2 +hibernate.connection.password=ebf_db_password2 \ No newline at end of file diff --git a/EbfEmployees/src/main/resources/hibernate-database-user.properties b/EbfEmployees/src/main/resources/hibernate-database-user.properties new file mode 100644 index 0000000..508f5b1 --- /dev/null +++ b/EbfEmployees/src/main/resources/hibernate-database-user.properties @@ -0,0 +1,4 @@ +hibernate.connection.driver_class=org.postgresql.Driver +hibernate.connection.url=jdbc:postgresql://localhost:54322/ebf_employees1 +hibernate.connection.username=ebf_db_user1 +hibernate.connection.password=ebf_db_password1 \ No newline at end of file diff --git a/EbfEmployees/src/test/resources/application.properties b/EbfEmployees/src/test/resources/application.properties deleted file mode 100644 index ff6fab7..0000000 --- a/EbfEmployees/src/test/resources/application.properties +++ /dev/null @@ -1,12 +0,0 @@ -logging.level.root=info -spring.datasource.initialization-mode=always -spring.datasource.platform=h2 -spring.datasource.driver-class-name=org.h2.Driver -spring.datasource.url=jdbc:h2:mem:ebf_db;DB_CLOSE_DELAY=-1 -spring.datasource.username=sa -spring.datasource.password=sa - -spring.flyway.enabled=false -spring.mvc.pathmatch.matching-strategy=ant_path_matcher -jwtauth.lifeTime = 7200000 -jwtauth.secret = ebfSecret \ No newline at end of file diff --git a/EbfEmployeesFront/package-lock.json b/EbfEmployeesFront/package-lock.json index 0ddca16..2a4ec1b 100644 --- a/EbfEmployeesFront/package-lock.json +++ b/EbfEmployeesFront/package-lock.json @@ -18,6 +18,7 @@ "@angular/platform-browser-dynamic": "~13.1.0", "@angular/router": "~13.1.0", "rxjs": "~7.4.0", + "sass": "^1.49.9", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -210,6 +211,22 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/@angular-devkit/build-angular/node_modules/sass": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.44.0.tgz", + "integrity": "sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/@angular-devkit/build-webpack": { "version": "0.1301.2", "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1301.2.tgz", @@ -2928,7 +2945,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3239,7 +3255,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -3325,7 +3340,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -3507,7 +3521,6 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -5407,7 +5420,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5571,7 +5583,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -5684,7 +5695,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -6060,8 +6070,7 @@ "node_modules/immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", - "dev": true + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==" }, "node_modules/import-fresh": { "version": "3.3.0", @@ -6279,7 +6288,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6333,7 +6341,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6351,7 +6358,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -6378,7 +6384,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -7756,7 +7761,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8377,7 +8381,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -10236,7 +10239,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -10580,19 +10582,19 @@ "dev": true }, "node_modules/sass": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.44.0.tgz", - "integrity": "sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==", - "dev": true, + "version": "1.49.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", + "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0" + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { "sass": "sass.js" }, "engines": { - "node": ">=8.9.0" + "node": ">=12.0.0" } }, "node_modules/sass-loader": { @@ -11017,7 +11019,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -11532,7 +11533,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -12403,6 +12403,16 @@ "dev": true } } + }, + "sass": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.44.0.tgz", + "integrity": "sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0" + } } } }, @@ -14386,7 +14396,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -14616,8 +14625,7 @@ "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, "bl": { "version": "4.1.0", @@ -14699,7 +14707,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -14832,7 +14839,6 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -16246,7 +16252,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -16370,7 +16375,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "optional": true }, "function-bind": { @@ -16449,7 +16453,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -16742,8 +16745,7 @@ "immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", - "dev": true + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==" }, "import-fresh": { "version": "3.3.0", @@ -16914,7 +16916,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -16946,8 +16947,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -16959,7 +16959,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -16979,8 +16978,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-path-cwd": { "version": "2.2.0", @@ -18007,8 +18005,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "normalize-range": { "version": "0.1.2", @@ -18479,8 +18476,7 @@ "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" }, "pify": { "version": "2.3.0", @@ -19877,7 +19873,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -20133,13 +20128,13 @@ "dev": true }, "sass": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.44.0.tgz", - "integrity": "sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==", - "dev": true, + "version": "1.49.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", + "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", "requires": { "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0" + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" } }, "sass-loader": { @@ -20475,8 +20470,7 @@ "source-map-js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", - "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", - "dev": true + "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==" }, "source-map-loader": { "version": "3.0.0", @@ -20851,7 +20845,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } diff --git a/EbfEmployeesFront/package.json b/EbfEmployeesFront/package.json index de2f52b..78228a7 100644 --- a/EbfEmployeesFront/package.json +++ b/EbfEmployeesFront/package.json @@ -20,6 +20,7 @@ "@angular/platform-browser-dynamic": "~13.1.0", "@angular/router": "~13.1.0", "rxjs": "~7.4.0", + "sass": "^1.49.9", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/EbfEmployeesFront/src/app/app.component.html b/EbfEmployeesFront/src/app/app.component.html index 29dbb20..277bd67 100644 --- a/EbfEmployeesFront/src/app/app.component.html +++ b/EbfEmployeesFront/src/app/app.component.html @@ -1,4 +1,6 @@ + + diff --git a/EbfEmployeesFront/src/app/app.component.ts b/EbfEmployeesFront/src/app/app.component.ts index d2fce75..bd6faa0 100644 --- a/EbfEmployeesFront/src/app/app.component.ts +++ b/EbfEmployeesFront/src/app/app.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { DatabaseSelectionService } from './services/database-selection.service'; @Component({ selector: 'app-root', @@ -7,4 +8,11 @@ import { Component } from '@angular/core'; }) export class AppComponent { title = 'EbfEmployeesFront'; + + constructor(private databaseSelectionService: DatabaseSelectionService){} + + public selectDatabase(database: string): void { + this.databaseSelectionService.setDatabase(database) + } } + diff --git a/EbfEmployeesFront/src/app/app.module.ts b/EbfEmployeesFront/src/app/app.module.ts index 74ca723..422ca5c 100644 --- a/EbfEmployeesFront/src/app/app.module.ts +++ b/EbfEmployeesFront/src/app/app.module.ts @@ -31,6 +31,7 @@ import { CreateCompanyDialogComponent } from './components/dialogs/create-compan import { CreateEmployeeDialogComponent } from './components/dialogs/create-employee-dialog/create-employee-dialog.component'; import { EmployeeDetailsComponent } from './components/employee-details/employee-details.component' import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatSortModule } from '@angular/material/sort'; @NgModule({ declarations: [ @@ -65,7 +66,8 @@ import { MatPaginatorModule } from '@angular/material/paginator'; MatSnackBarModule, MatTableModule, MatIconModule, - MatPaginatorModule + MatPaginatorModule, + MatSortModule ], providers: [{ provide: HTTP_INTERCEPTORS, useClass: HttpInterceptorService, multi: true }], bootstrap: [AppComponent], diff --git a/EbfEmployeesFront/src/app/services/database-selection.service.ts b/EbfEmployeesFront/src/app/services/database-selection.service.ts new file mode 100644 index 0000000..04e9ee5 --- /dev/null +++ b/EbfEmployeesFront/src/app/services/database-selection.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@angular/core"; + +@Injectable({ providedIn: 'root' }) +export class DatabaseSelectionService { + public setDatabase(database: string): void { + sessionStorage.setItem('database', database) + } + + public getDatabase(): string | null { + return sessionStorage.getItem('database'); + } +} \ No newline at end of file diff --git a/EbfEmployeesFront/src/app/services/http-interceptor.service.ts b/EbfEmployeesFront/src/app/services/http-interceptor.service.ts index da53a7f..c9040d7 100644 --- a/EbfEmployeesFront/src/app/services/http-interceptor.service.ts +++ b/EbfEmployeesFront/src/app/services/http-interceptor.service.ts @@ -1,16 +1,17 @@ import { AccessTokenModel } from './../models/access-token.model'; import { LoginDialogComponent } from './../components/login-dialog/login-dialog.component'; -import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http"; +import { HttpEvent, HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { MatDialog } from "@angular/material/dialog"; import { catchError, filter, Observable, switchMap, take, throwError } from "rxjs"; import { StorageService } from "./storage.service"; import { MatSnackBar } from '@angular/material/snack-bar'; +import { DatabaseSelectionService } from './database-selection.service'; @Injectable({ providedIn: 'root' }) export class HttpInterceptorService implements HttpInterceptor { - constructor(private storageService: StorageService, private matDialog: MatDialog, private snackBar: MatSnackBar) { } + constructor(private storageService: StorageService, private matDialog: MatDialog, private snackBar: MatSnackBar, private databaseSelectionService: DatabaseSelectionService) { } intercept(req: HttpRequest, next: HttpHandler): Observable> { return next.handle(this.cloneRequestWithAccessToken(req)).pipe(catchError(e => { @@ -36,13 +37,17 @@ export class HttpInterceptorService implements HttpInterceptor { cloneRequestWithAccessToken(request: HttpRequest): HttpRequest { if (this.storageService.getAccessToken() != null) { - + let httph = new HttpHeaders({'Authorization': `Bearer ${this.storageService.getAccessToken()}`}); + if(this.databaseSelectionService.getDatabase() != null){ + let database = this.databaseSelectionService.getDatabase() as string; + httph = new HttpHeaders({'Authorization': `Bearer ${this.storageService.getAccessToken()}`, + 'Selected-Database': database}); + } + return request.clone( { withCredentials: true, - setHeaders: { - Authorization: `Bearer ${this.storageService.getAccessToken()}` - } + headers: httph } ) } else { diff --git a/db-test.yml b/db-test.yml new file mode 100644 index 0000000..8e91199 --- /dev/null +++ b/db-test.yml @@ -0,0 +1,25 @@ +version: '2' + +services: + + db1: + image: 'postgres:13.1-alpine' + container_name: db1 + environment: + - POSTGRES_DB=ebf_employees1 + - POSTGRES_USER=ebf_db_user1 + - POSTGRES_PASSWORD=ebf_db_password1 + - APP_DB_NAME=ebf_employees1 + ports: + - "54322:5432" + + db2: + image: 'postgres:13.1-alpine' + container_name: db2 + environment: + - POSTGRES_DB=ebf_employees2 + - POSTGRES_USER=ebf_db_user2 + - POSTGRES_PASSWORD=ebf_db_password2 + - APP_DB_NAME=ebf_employees2 + ports: + - "5432:5432"