Skip to content

Commit

Permalink
feat: use uPortal soffit
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentin-Guillemin committed Nov 9, 2023
1 parent 372e653 commit 781605a
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ VITE_BASE_URI="/"
VITE_API_URI="/"
VITE_REFRESH_IDENTITY_MILLISECONDS=10000

VITE_USER_INFO_API_URI=""

VITE_PROXY_API_URL="http://localhost:8090"
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@types/lodash.debounce": "^4.0.8",
"@types/lodash.isempty": "^4.4.8",
"@types/node": "^20.8.10",
"@uportal/open-id-connect": "^1.40.2",
"@vitejs/plugin-vue": "^4.4.0",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
Expand All @@ -57,6 +58,7 @@
"lodash.isempty": "^4.4.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.3",
"regenerator-runtime": "^0.14.0",
"resize-observer-polyfill": "^1.5.1",
"sass": "^1.69.5",
"typescript": "~5.2.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 GIP-RECIA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.recia.collabsoft.configuration;

import fr.recia.collabsoft.interceptors.SoffitInterceptor;
import fr.recia.collabsoft.interceptors.beans.SoffitHolder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(soffitInterceptor());
}

@Bean
public SoffitInterceptor soffitInterceptor() {
return new SoffitInterceptor(soffitHolder());
}

@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public SoffitHolder soffitHolder() {
return new SoffitHolder();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2023 GIP-RECIA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.recia.collabsoft.interceptors;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.recia.collabsoft.interceptors.beans.SoffitHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;
import java.util.Map;

@Slf4j
public class SoffitInterceptor implements HandlerInterceptor {

private final SoffitHolder soffitHolder;

public SoffitInterceptor(SoffitHolder soffitHolder) {
this.soffitHolder = soffitHolder;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null) {
log.debug("No Authorization header found");
soffitHolder.setSub("");
return true;
}

Base64.Decoder decoder = Base64.getUrlDecoder();
String payload = new String(decoder.decode(token.replace("Bearer ", "").split("\\.")[1]));

Map<String, String> soffit = null;
ObjectMapper objectMapper = new ObjectMapper();
try {
soffit = objectMapper.readValue(payload, new TypeReference<>() {
});
soffitHolder.setSub(soffit.get("sub"));
} catch (IOException ignored) {
}
log.debug("Soffit : {}", soffit);
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 GIP-RECIA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.recia.collabsoft.interceptors.beans;

import lombok.Data;

@Data
public class SoffitHolder {

private String sub;

}
16 changes: 10 additions & 6 deletions src/main/java/fr/recia/collabsoft/web/rest/FileController.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import fr.recia.collabsoft.db.repositories.FileRepository;
import fr.recia.collabsoft.db.repositories.MetadataRepository;
import fr.recia.collabsoft.db.repositories.UserRepository;
import fr.recia.collabsoft.interceptors.beans.SoffitHolder;
import fr.recia.collabsoft.pojo.JsonCollaborationBody;
import fr.recia.collabsoft.pojo.JsonFileBody;
import fr.recia.collabsoft.pojo.JsonHistoryBody;
Expand Down Expand Up @@ -73,12 +74,15 @@ public class FileController {
@Inject
private UserRepository<User> userRepository;

// TODO: only for tests
private final Long myId = 1L;
private final SoffitHolder soffitHolder;

public FileController(SoffitHolder soffitHolder) {
this.soffitHolder = soffitHolder;
}

private User getCurrentUser() {
return userRepository.findOne(
QUser.user.id.eq(myId)
QUser.user.casUid.eq(soffitHolder.getSub())
).orElse(null);
}

Expand All @@ -93,7 +97,7 @@ private User getCurrentUser() {
@GetMapping
public ResponseEntity<List<File>> getFiles() {
final List<File> files = IteratorUtils.toList(
fileRepository.findAll(QFile.file.creator.id.eq(myId)).iterator()
fileRepository.findAll(QFile.file.creator.casUid.eq(soffitHolder.getSub())).iterator()
);
if (files.isEmpty()) return new ResponseEntity<>(HttpStatus.NOT_FOUND);

Expand Down Expand Up @@ -131,7 +135,7 @@ public ResponseEntity<List<File>> getSharedFiles() {
QFile.file.id.in(
JPAExpressions.select(QCollaboration.collaboration.file.id)
.from(QCollaboration.collaboration)
.where(QCollaboration.collaboration.user.id.eq(myId))
.where(QCollaboration.collaboration.user.casUid.eq(soffitHolder.getSub()))
)
).iterator()
);
Expand Down Expand Up @@ -255,7 +259,7 @@ public ResponseEntity<List<Collaboration>> putMetadata(@PathVariable Long id, @N
if (!body.putDataOk()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
final User user = getCurrentUser();
Metadata metadata = metadataRepository.findOne(
QMetadata.metadata.file.id.eq(id).and(QMetadata.metadata.user.id.eq(myId))
QMetadata.metadata.file.id.eq(id).and(QMetadata.metadata.user.casUid.eq(soffitHolder.getSub()))
).orElse(null);
if (metadata == null) {
final File file = fileRepository.findOne(
Expand Down
2 changes: 2 additions & 0 deletions src/main/webapp/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'regenerator-runtime/runtime.js';

import { createApp } from 'vue';

import { register as registerDirectives } from '@/directives';
Expand Down
11 changes: 11 additions & 0 deletions src/main/webapp/src/utils/axiosUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import i18n from '@/plugins/i18n';
import { getToken } from '@/utils/soffitUtils';
import axios from 'axios';
import { useToast } from 'vue-toastification';

Expand All @@ -12,6 +13,16 @@ const instance = axios.create({
timeout: 10000,
});

instance.interceptors.request.use(async (config) => {
try {
config.headers['Authorization'] = `Bearer ${(await getToken()).encoded}`;
} catch (e) {
// nothing to do
}

return config;
});

const errorHandler = (e: any, toastOrI18n?: boolean | string): void => {
const showToast: boolean = typeof toastOrI18n == 'boolean' && toastOrI18n;

Expand Down
11 changes: 11 additions & 0 deletions src/main/webapp/src/utils/soffitUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import oidc, { type JWT } from '@uportal/open-id-connect';

const { VITE_USER_INFO_API_URI } = import.meta.env;

const getToken = async (): Promise<{ encoded: string; decoded: JWT }> => {
const { encoded, decoded } = await oidc({ userInfoApiUrl: VITE_USER_INFO_API_URI });

return { encoded, decoded };
};

export { getToken };
18 changes: 18 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,15 @@ __metadata:
languageName: node
linkType: hard

"@uportal/open-id-connect@npm:^1.40.2":
version: 1.40.2
resolution: "@uportal/open-id-connect@npm:1.40.2"
dependencies:
jwt-decode: "npm:^3.0.0"
checksum: 0e16ad2b0725e0eb1340ba30b0f2d3b7580e7a494364bfc4fab736431616738073d9125a8c6b83544c6f49974928756e2130d04932dc1e78fc81ac629c334519
languageName: node
linkType: hard

"@vitejs/plugin-vue@npm:^4.4.0":
version: 4.4.0
resolution: "@vitejs/plugin-vue@npm:4.4.0"
Expand Down Expand Up @@ -2075,6 +2084,7 @@ __metadata:
"@types/lodash.debounce": "npm:^4.0.8"
"@types/lodash.isempty": "npm:^4.4.8"
"@types/node": "npm:^20.8.10"
"@uportal/open-id-connect": "npm:^1.40.2"
"@vitejs/plugin-vue": "npm:^4.4.0"
"@vue/eslint-config-prettier": "npm:^8.0.0"
"@vue/eslint-config-typescript": "npm:^12.0.0"
Expand All @@ -2095,6 +2105,7 @@ __metadata:
npm-run-all: "npm:^4.1.5"
pinia: "npm:^2.1.7"
prettier: "npm:^3.0.3"
regenerator-runtime: "npm:^0.14.0"
resize-observer-polyfill: "npm:^1.5.1"
sass: "npm:^1.69.5"
typescript: "npm:~5.2.2"
Expand Down Expand Up @@ -4193,6 +4204,13 @@ __metadata:
languageName: node
linkType: hard

"jwt-decode@npm:^3.0.0":
version: 3.1.2
resolution: "jwt-decode@npm:3.1.2"
checksum: 20a4b072d44ce3479f42d0d2c8d3dabeb353081ba4982e40b83a779f2459a70be26441be6c160bfc8c3c6eadf9f6380a036fbb06ac5406b5674e35d8c4205eeb
languageName: node
linkType: hard

"keyv@npm:^4.5.3":
version: 4.5.4
resolution: "keyv@npm:4.5.4"
Expand Down

0 comments on commit 781605a

Please sign in to comment.