- implemented simulated user in dev profile

This commit is contained in:
Jan 2025-10-05 13:15:26 +02:00
parent 4c34a1bade
commit 5760c7d30c
14 changed files with 196 additions and 125 deletions

View file

@ -6,9 +6,9 @@
<div class="edit-calculation-cell--material copyable-cell"
@click="action('material')">
<div class="edit-calculation-cell-line">{{ premise.material.part_number }}</div>
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.material.name">
{{ premise.material.name }}
</div>
<!-- <div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.material.name">-->
<!-- {{ premise.material.name }}-->
<!-- </div>-->
<div class="edit-calculation-cell-line edit-calculation-cell-subline" v-if="premise.hs_code">
HS Code:
{{ premise.material.hs_code }}

View file

@ -8,7 +8,7 @@
<modal :state="modalSelectMaterial" @close="closeEditModal">
<select-material :part-number="partNumber" @close="modalEditClick"/>
</modal>
<icon-button icon="pencil-simple" @click="activateEditMode"></icon-button>
<!-- <icon-button icon="pencil-simple" @click="activateEditMode"></icon-button>-->
</div>
</div>

View file

@ -1,7 +1,7 @@
<template>
<div class="container" :class="{ 'responsive': responsive }" @focusout="focusLost">
<div class="caption-column">Length</div>
<div class="caption-column">HU length</div>
<div class="input-column">
<div class="text-container">
<input ref="lengthInput" :value="huLength" @blur="validateDimension('length', $event)"
@ -15,7 +15,7 @@
</div>
<div class="caption-column">Width</div>
<div class="caption-column">HU width</div>
<div class="input-column">
<div class="text-container">
<input ref="widthInput" :value="huWidth" @blur="validateDimension('width', $event)"
@ -27,7 +27,7 @@
<div class="input-column"></div>
<div class="caption-column">Height</div>
<div class="caption-column">HU height</div>
<div class="input-column">
<div class="text-container">
<input ref="heightInput" :value="huHeight" @blur="validateDimension('height', $event)"
@ -39,7 +39,7 @@
<div class="input-column"></div>
<div class="caption-column">Weight</div>
<div class="caption-column">HU weight</div>
<div class="input-column">
<div class="text-container">
<input ref="weightInput" :value="huWeight" @blur="validateWeight('weight', $event)"

View file

@ -22,8 +22,8 @@
<modal :state="selectSupplierModalState" @close="closeEditModal">
<select-node @update-supplier="modalDialogClose"></select-node>
</modal>
<icon-button icon="plus" @click="openModal"></icon-button>
<icon-button icon="pencil-simple" @click="openModal"></icon-button>
<!-- <icon-button icon="plus" @click="openModal"></icon-button>-->
<!-- <icon-button icon="pencil-simple" @click="openModal"></icon-button>-->
</div>
</div>
</template>

View file

@ -3,7 +3,8 @@
<div class="header-container">
<h2 class="page-header">Edit calculation</h2>
<div class="header-controls">
<basic-button @click="close" :show-icon="false" :disabled="premiseEditStore.selectedLoading" variant="secondary"> {{ fromMassEdit ? 'Back' : 'Close' }}
<basic-button @click="close" :show-icon="false" :disabled="premiseEditStore.selectedLoading"
variant="secondary"> {{ fromMassEdit ? 'Back' : 'Close' }}
</basic-button>
<basic-button v-if="!fromMassEdit"
:show-icon="true"
@ -16,7 +17,7 @@
</div>
</div>
<Toast ref="toast" />
<Toast ref="toast"/>
<div v-if="premiseEditStore.selectedLoading" class="edit-calculation-spinner-container">
<box class="edit-calculation-spinner">
@ -40,7 +41,18 @@
</box>
</div>
<h3 class="sub-header">Master data</h3>
<!-- Headers for desktop (above 1500px) -->
<div class="master-data-container master-data-headers-desktop">
<h3 class="sub-header">Material</h3>
<h3 class="sub-header">Price</h3>
<h3 class="sub-header">Handling unit</h3>
</div>
<!-- Header for mobile (below 1500px) -->
<div class="master-data-container master-data-headers-mobile">
<h3 class="sub-header">Master data</h3>
</div>
<div class="master-data-container">
<box class="master-data-item">
<material-edit :part-number="premise.material.part_number"
@ -135,8 +147,7 @@ export default {
const error = await this.premiseEditStore.startCalculation();
if(error !== null) {
if (error !== null) {
this.$refs.toast.addToast({
icon: 'warning',
@ -145,18 +156,16 @@ export default {
variant: 'exception',
duration: 8000
})
} else
{
} else {
this.close();
}
},
close() {
if(this.bulkEditQuery) {
if (this.bulkEditQuery) {
//TODO: deselect and save
this.premiseEditStore.deselectPremise();
this.$router.push({name: 'bulk', params: {ids: this.bulkEditQuery}});
}
else {
} else {
//TODO: deselect and save
this.$router.push({name: 'home'});
}
@ -164,11 +173,11 @@ export default {
async save(type) {
let success = false;
if(type === 'price') {
if (type === 'price') {
success = await this.premiseEditStore.savePrice();
} else if(type === 'material') {
} else if (type === 'material') {
success = await this.premiseEditStore.saveMaterial();
} else if(type === 'packaging') {
} else if (type === 'packaging') {
success = await this.premiseEditStore.savePackaging();
}
@ -198,7 +207,7 @@ export default {
created() {
[this.id] = new UrlSafeBase64().decodeIds(this.$route.params.id);
if(this.$route.params.ids) {
if (this.$route.params.ids) {
this.bulkEditQuery = this.$route.params.ids;
this.premiseEditStore.selectSinglePremise(this.id, new UrlSafeBase64().decodeIds(this.$route.params.ids));
} else {
@ -248,11 +257,29 @@ export default {
padding: 2.4rem;
}
/* Show desktop headers by default */
.master-data-headers-desktop {
display: grid;
}
.master-data-headers-mobile {
display: none;
}
/* Responsive layout for viewports below 1500px */
@media (max-width: 1500px) {
.master-data-container {
grid-template-columns: 1fr;
}
/* Hide desktop headers and show mobile header */
.master-data-headers-desktop {
display: none;
}
.master-data-headers-mobile {
display: grid;
}
}
.trace-link {

View file

@ -0,0 +1,115 @@
package de.avatic.lcc.config;
import de.avatic.lcc.model.users.User;
import de.avatic.lcc.repositories.users.UserRepository;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Profile;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Profile("dev | test")
public class DevUserEmulationFilter extends OncePerRequestFilter {
private static final String DEV_USER_ID_SESSION_KEY = "dev.emulated.user.id";
private final UserRepository userRepository;
public DevUserEmulationFilter(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
protected void doFilterInternal(@NotNull HttpServletRequest request,
@NotNull HttpServletResponse response,
@NotNull FilterChain filterChain) throws ServletException, IOException {
HttpSession session = request.getSession(true);
Integer emulatedUserId = (Integer) session.getAttribute(DEV_USER_ID_SESSION_KEY);
if(emulatedUserId != null) {
User user = userRepository.getById(emulatedUserId == null ? 1 : emulatedUserId);
if (user != null) {
setEmulatedUser(user);
}
}
filterChain.doFilter(request, response);
}
private void setEmulatedUser(User user) {
Set<GrantedAuthority> authorities = new HashSet<>();
user.getGroups().forEach(group ->
authorities.add(new SimpleGrantedAuthority("ROLE_" + group.getName().toUpperCase()))
);
// Create a mock OIDC user
Map<String, Object> claims = new HashMap<>();
claims.put("sub", user.getId().toString());
claims.put("email", user.getEmail());
claims.put("preferred_username", user.getEmail());
claims.put("name", user.getFirstName() + " " + user.getLastName());
if (user.getWorkdayId() != null) {
claims.put("workday_id", user.getWorkdayId());
}
OidcIdToken idToken = new OidcIdToken(
"mock-token",
Instant.now(),
Instant.now().plusSeconds(3600),
claims
);
OidcUserInfo userInfo = new OidcUserInfo(claims);
LccOidcUser oidcUser = new LccOidcUser(
authorities,
idToken,
userInfo,
"preferred_username",
user.getId()
);
Authentication authentication = new PreAuthenticatedAuthenticationToken(
oidcUser,
null,
authorities
);
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
public static void setEmulatedUserId(HttpSession session, Integer userId) {
if (userId == null) {
session.removeAttribute(DEV_USER_ID_SESSION_KEY);
} else {
session.setAttribute(DEV_USER_ID_SESSION_KEY, userId);
}
}
public static Integer getEmulatedUserId(HttpSession session) {
return (Integer) session.getAttribute(DEV_USER_ID_SESSION_KEY);
}
}

View file

@ -58,14 +58,18 @@ public class SecurityConfig {
@Bean
@Profile("dev | test")
public SecurityFilterChain devSecurityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain devSecurityFilterChain(HttpSecurity http, UserRepository userRepository) throws Exception {
return http
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/**").permitAll()
.requestMatchers("/api/dev/**").permitAll()
.anyRequest().permitAll())
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new LccCsrfTokenRequestHandler())
)
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(new DevUserEmulationFilter(userRepository), BasicAuthenticationFilter.class)
.build();
}

View file

@ -7,11 +7,13 @@ import de.avatic.lcc.dto.bulk.BulkOperationDTO;
import de.avatic.lcc.service.bulk.BulkOperationService;
import de.avatic.lcc.service.bulk.TemplateExportService;
import de.avatic.lcc.util.exception.base.BadRequestException;
import jakarta.annotation.security.RolesAllowed;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@ -24,6 +26,7 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/bulk")
@RolesAllowed({"ROLE_SUPER", "ROLE_FREIGHT", "ROLE_PACKAGING"})
public class BulkOperationController {
private final BulkOperationService bulkOperationService;
@ -34,20 +37,17 @@ public class BulkOperationController {
this.templateExportService = templateExportService;
}
@GetMapping({"/status/", "/status"})
public ResponseEntity<List<BulkOperationDTO>> getBulkStatus() {
return ResponseEntity.ok(bulkOperationService.getStatus());
}
@PostMapping({"/upload/{type}", "/upload/{type}/"})
public ResponseEntity<Void> uploadFile(@PathVariable BulkFileType type, @BodyParam("file") MultipartFile file) {
bulkOperationService.processFileImport(type, file);
return ResponseEntity.ok().build();
}
@GetMapping({"/templates/{type}", "/templates/{type}/"})
public ResponseEntity<InputStreamResource> generateTemplate(@PathVariable BulkFileType type) {
HttpHeaders headers = new HttpHeaders();

View file

@ -1,6 +1,6 @@
package de.avatic.lcc.controller.dev;
import com.azure.core.annotation.BodyParam;
import de.avatic.lcc.config.DevUserEmulationFilter;
import de.avatic.lcc.dto.error.CalculationJobDumpDTO;
import de.avatic.lcc.dto.users.UserDTO;
import de.avatic.lcc.repositories.error.DumpRepository;
@ -8,7 +8,7 @@ import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import de.avatic.lcc.repositories.users.UserRepository;
import de.avatic.lcc.service.transformer.users.UserTransformer;
import de.avatic.lcc.service.users.authorization.AuthorizationService;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.constraints.Min;
import org.springframework.context.annotation.Profile;
import org.springframework.http.ResponseEntity;
@ -21,13 +21,12 @@ import java.util.List;
@RequestMapping({"/api/dev", "/api/dev/"})
public class DevController {
private final AuthorizationService authorizationService;
private final DumpRepository dumpRepository;
private final UserRepository userRepository;
private final UserTransformer userTransformer;
public DevController(AuthorizationService authorizationService, DumpRepository dumpRepository, UserRepository userRepository, UserTransformer userTransformer) {
this.authorizationService = authorizationService;
public DevController(DumpRepository dumpRepository, UserRepository userRepository, UserTransformer userTransformer) {
this.dumpRepository = dumpRepository;
this.userRepository = userRepository;
this.userTransformer = userTransformer;
@ -67,8 +66,8 @@ public class DevController {
}
@PostMapping({"/user", "/user/"})
public ResponseEntity<Void> setActiveUser(@RequestBody UserDTO user) {
authorizationService.setActiveUser(user.getEmail());
public ResponseEntity<Void> setActiveUser(@RequestBody UserDTO user, HttpSession session) {
DevUserEmulationFilter.setEmulatedUserId(session, userRepository.getByEmail(user.getEmail()).getId());
return ResponseEntity.ok().build();
}

View file

@ -1,9 +1,12 @@
package de.avatic.lcc.controller.users;
import de.avatic.lcc.config.LccOidcUser;
import de.avatic.lcc.dto.users.UserDTO;
import de.avatic.lcc.repositories.users.UserRepository;
import de.avatic.lcc.service.transformer.users.UserTransformer;
import de.avatic.lcc.service.users.authorization.AuthorizationService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -13,17 +16,24 @@ import org.springframework.web.bind.annotation.RestController;
public class ActiveUserController {
private final AuthorizationService authorizationService;
private final UserTransformer userTransformer;
private final UserRepository userRepository;
public ActiveUserController(UserTransformer userTransformer, UserRepository userRepository) {
public ActiveUserController(AuthorizationService authorizationService, UserTransformer userTransformer) {
this.authorizationService = authorizationService;
this.userTransformer = userTransformer;
this.userRepository = userRepository;
}
@GetMapping
public ResponseEntity<UserDTO> getActiveUser() {
return ResponseEntity.ok(userTransformer.toUserDTO(authorizationService.getActiveUser()));
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth.getPrincipal() instanceof LccOidcUser) {
LccOidcUser user = (LccOidcUser) auth.getPrincipal();
return ResponseEntity.ok(userTransformer.toUserDTO(userRepository.getById(user.getSqlUserId())));
}
return ResponseEntity.internalServerError().build();
}
}

View file

@ -159,27 +159,6 @@ public class MaterialRepository {
return Optional.ofNullable(jdbcTemplate.update(updateQuery, material.getName(), material.getPartNumber(), material.getNormalizedPartNumber(), material.getHsCode(), material.getId()) == 0 ? null : material.getId());
}
@Transactional
public Optional<Integer> create(Material material) {
//todo if there is a deprecated, reuse this
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement("INSERT INTO material (name, part_number, normalized_part_number, hs_code) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
ps.setString(1, material.getName()); //
ps.setString(2, material.getPartNumber());
ps.setString(3, material.getNormalizedPartNumber());
ps.setString(4, material.getHsCode());
return ps;
}, keyHolder);
return Optional.ofNullable(!Objects.requireNonNull(keyHolder.getKeys()).isEmpty() ? ((Integer) keyHolder.getKeys().values().iterator().next()) : null);
}
/**
* Returns all IDs from the input list that don't exist in the material table
* @param ids List of integers to check

View file

@ -1,12 +0,0 @@
package de.avatic.lcc.service.users.authorization;
import de.avatic.lcc.dto.users.UserDTO;
import de.avatic.lcc.model.users.User;
import org.springframework.stereotype.Service;
@Service
public interface AuthorizationService {
default void setActiveUser(String id) { throw new UnsupportedOperationException(); }
User getActiveUser();
}

View file

@ -1,15 +0,0 @@
package de.avatic.lcc.service.users.authorization;
import de.avatic.lcc.model.users.User;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("!dev & !test")
public class DefaultAuthorizationService implements AuthorizationService{
@Override
public User getActiveUser() {
return null;
}
}

View file

@ -1,36 +0,0 @@
package de.avatic.lcc.service.users.authorization;
import de.avatic.lcc.dto.users.UserDTO;
import de.avatic.lcc.model.users.User;
import de.avatic.lcc.repositories.users.UserRepository;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("dev | test")
public class SimulatedAuthorizationService implements AuthorizationService {
private final UserRepository userRepository;
private User activeUser;
public SimulatedAuthorizationService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void setActiveUser(String mail) {
this.activeUser = userRepository.getByEmail(mail);
System.out.println(activeUser);
}
@Override
public User getActiveUser() {
if(activeUser == null){
activeUser = userRepository.getById(1);
}
return activeUser;
}
}