commit
6d754fee91
.gitea/workflows
DockerfileREADME.mdcompose-branch-deploy.yamlcore/src
main
java/se/su/dsv/scipro/forum
BasicForumService.javaBasicForumServiceImpl.javaForumPostRepository.javaForumPostRepositoryImpl.javaProjectForumService.javaProjectForumServiceImpl.java
resources/db/migration
test/java/se/su/dsv/scipro/forum
view/src/main/java/se/su/dsv/scipro
forum/panels
supervisor/panels
war
14
.gitea/workflows/deploy-branch-cleanup.yaml
Normal file
14
.gitea/workflows/deploy-branch-cleanup.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
name: Remove branch deployment from branch.dsv.su.se
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://gitea.dsv.su.se/ansv7779/action-branch-deploy@v2
|
||||
with:
|
||||
cleanup-ssh-key: ${{ secrets.BRANCH_CLEANUP_KEY }}
|
||||
compose-file: compose-branch-deploy.yaml
|
||||
mode: 'cleanup'
|
26
.gitea/workflows/deploy-branch.yaml
Normal file
26
.gitea/workflows/deploy-branch.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
name: Deploy to branch.dsv.su.se
|
||||
on:
|
||||
- pull_request
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: deploy
|
||||
uses: https://gitea.dsv.su.se/ansv7779/action-branch-deploy@v2
|
||||
with:
|
||||
ssh-key: ${{ secrets.BRANCH_DEPLOY_KEY }}
|
||||
compose-file: compose-branch-deploy.yaml
|
||||
- name: Post URL to deployment as comment
|
||||
uses: actions/github-script@v7
|
||||
if: github.event.action == 'opened'
|
||||
env:
|
||||
BRANCH_URL: ${{ steps.deploy.outputs.url }}
|
||||
with:
|
||||
script: |
|
||||
const url = process.env.BRANCH_URL;
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `Deployed to ${url}`
|
||||
})
|
39
Dockerfile
Normal file
39
Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
FROM debian:bookworm AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y openjdk-17-jdk-headless
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY pom.xml .
|
||||
COPY .mvn/ .mvn/
|
||||
COPY mvnw .
|
||||
COPY api/pom.xml api/pom.xml
|
||||
COPY core/pom.xml core/pom.xml
|
||||
COPY view/pom.xml view/pom.xml
|
||||
COPY war/pom.xml war/pom.xml
|
||||
COPY daisy-integration/pom.xml daisy-integration/pom.xml
|
||||
|
||||
# Download dependencies in a separate layer to allow caching for future builds
|
||||
RUN ./mvnw dependency:go-offline \
|
||||
--batch-mode \
|
||||
--define includeScope=compile \
|
||||
--activate-profiles docker-dependencies
|
||||
|
||||
COPY api/src/ api/src/
|
||||
COPY core/src/ core/src/
|
||||
COPY view/src/ view/src/
|
||||
COPY war/src/ war/src/
|
||||
COPY daisy-integration/src/ daisy-integration/src/
|
||||
|
||||
RUN ./mvnw package \
|
||||
--offline \
|
||||
--define skipTests \
|
||||
--activate-profiles branch,DEV \
|
||||
--define skip.npm \
|
||||
--define skip.installnodenpm
|
||||
|
||||
FROM tomcat:10 AS run
|
||||
|
||||
COPY --from=build /app/war/target/*.war /usr/local/tomcat/webapps/ROOT.war
|
||||
|
||||
EXPOSE 8080
|
10
README.md
10
README.md
@ -39,3 +39,13 @@ can be performed.
|
||||
Go to `Settings -> Language & Frameworks -> JavaScript -> Prettier` and then check
|
||||
`Automatic Prettier Configuration`, set `Run for files` to `**/*.{java}`,
|
||||
and finally check `Run on save`.
|
||||
|
||||
## Test servers
|
||||
All pull requests are automatically deployed to a test server.
|
||||
The URL to the test server will be posted as a comment in the pull request once deployed.
|
||||
|
||||
Prepare test data in the `DataInitializer` class to help others test your changes.
|
||||
Document (in the pull request) which users to log in as and what to do to see the changes.
|
||||
|
||||
If you want to reset the data to its original state you can re-run the "deploy-branch.yaml"
|
||||
workflow at https://gitea.dsv.su.se/DMC/scipro/actions for the branch you want to reset.
|
||||
|
72
compose-branch-deploy.yaml
Normal file
72
compose-branch-deploy.yaml
Normal file
@ -0,0 +1,72 @@
|
||||
services:
|
||||
scipro:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
oauth2:
|
||||
condition: service_started
|
||||
environment:
|
||||
- JDBC_DATABASE_URL=jdbc:mariadb://db:3306/scipro
|
||||
- JDBC_DATABASE_USERNAME=scipro
|
||||
- JDBC_DATABASE_PASSWORD=scipro
|
||||
- OAUTH2_AUTHORIZATION_URI=https://oauth2-${VHOST}/authorize
|
||||
- OAUTH2_TOKEN_URI=https://oauth2-${VHOST}/exchange
|
||||
- OAUTH2_USER_INFO_URI=https://oauth2-${VHOST}/verify
|
||||
- OAUTH2_CLIENT_ID=scipro_client
|
||||
- OAUTH2_CLIENT_SECRET=scipro_secret
|
||||
- OAUTH2_RESOURCE_SERVER_ID=scipro_api_client
|
||||
- OAUTH2_RESOURCE_SERVER_SECRET=scipro_api_secret
|
||||
- OAUTH2_RESOURCE_SERVER_INTROSPECTION_URI=https://oauth2-${VHOST}/introspect
|
||||
networks:
|
||||
- traefik
|
||||
- internal
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${VHOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt"
|
||||
|
||||
db:
|
||||
image: mariadb:10.11
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- internal
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: root
|
||||
MARIADB_DATABASE: scipro
|
||||
MARIADB_USER: scipro
|
||||
MARIADB_PASSWORD: scipro
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect"]
|
||||
start_period: 10s
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 6
|
||||
|
||||
oauth2:
|
||||
build:
|
||||
context: https://github.com/dsv-su/toker.git
|
||||
dockerfile: embedded.Dockerfile
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- CLIENT_ID=scipro_client
|
||||
- CLIENT_SECRET=scipro_secret
|
||||
- CLIENT_REDIRECT_URI=https://${VHOST}/login/oauth2/code/scipro
|
||||
- RESOURCE_SERVER_ID=scipro_api_client
|
||||
- RESOURCE_SERVER_SECRET=scipro_api_secret
|
||||
networks:
|
||||
- traefik
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.oauth2-${COMPOSE_PROJECT_NAME}.rule=Host(`oauth2-${VHOST}`)"
|
||||
- "traefik.http.routers.oauth2-${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt"
|
||||
|
||||
networks:
|
||||
traefik:
|
||||
name: traefik
|
||||
external: true
|
||||
internal:
|
||||
name: ${COMPOSE_PROJECT_NAME}_internal
|
@ -1,7 +1,8 @@
|
||||
package se.su.dsv.scipro.forum;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
|
||||
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
@ -20,4 +21,6 @@ public interface BasicForumService extends Serializable {
|
||||
List<ForumPost> getPosts(ForumThread forumThread);
|
||||
|
||||
ForumThread createThread(String subject);
|
||||
|
||||
long countUnreadThreads(List<ForumThread> forumThreadList, User user);
|
||||
}
|
||||
|
@ -87,6 +87,11 @@ public class BasicForumServiceImpl implements BasicForumService {
|
||||
return threadRepository.save(forumThread);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countUnreadThreads(List<ForumThread> forumThreadList, User user) {
|
||||
return postRepository.countUnreadThreads(forumThreadList, user);
|
||||
}
|
||||
|
||||
private ForumPostReadState getReadState(final User user, final ForumPost post) {
|
||||
ForumPostReadState state = readStateRepository.find(user, post);
|
||||
if (state == null) {
|
||||
|
@ -8,6 +8,7 @@ import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
import se.su.dsv.scipro.system.JpaRepository;
|
||||
import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
import se.su.dsv.scipro.util.Pair;
|
||||
|
||||
@Transactional
|
||||
@ -15,4 +16,6 @@ public interface ForumPostRepository extends JpaRepository<ForumPost, Long>, Que
|
||||
List<ForumPost> findByThread(ForumThread forumThread);
|
||||
|
||||
List<Pair<ProjectThread, ForumPost>> latestPost(Project project, int amount);
|
||||
|
||||
long countUnreadThreads(List<ForumThread> forumThreadList, User user);
|
||||
}
|
||||
|
@ -2,19 +2,22 @@ package se.su.dsv.scipro.forum;
|
||||
|
||||
import static com.querydsl.core.types.dsl.Expressions.allOf;
|
||||
|
||||
import com.querydsl.jpa.JPAExpressions;
|
||||
import com.querydsl.jpa.impl.JPAQuery;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
|
||||
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
|
||||
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
|
||||
import se.su.dsv.scipro.forum.dataobjects.QForumPost;
|
||||
import se.su.dsv.scipro.forum.dataobjects.QForumPostReadState;
|
||||
import se.su.dsv.scipro.forum.dataobjects.QForumThread;
|
||||
import se.su.dsv.scipro.forum.dataobjects.QProjectThread;
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
import se.su.dsv.scipro.system.GenericRepo;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
import se.su.dsv.scipro.util.Pair;
|
||||
|
||||
public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implements ForumPostRepository {
|
||||
@ -44,4 +47,24 @@ public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implem
|
||||
.map(tuple -> new Pair<>(tuple.get(QProjectThread.projectThread), tuple.get(QForumPost.forumPost)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countUnreadThreads(List<ForumThread> forumThreadList, User user) {
|
||||
return new JPAQuery<>(em())
|
||||
.select(QForumThread.forumThread.id.countDistinct())
|
||||
.from(QForumThread.forumThread)
|
||||
.leftJoin(QForumThread.forumThread.posts, QForumPost.forumPost)
|
||||
.where(
|
||||
QForumPost.forumPost.notIn(
|
||||
JPAExpressions.select(QForumPostReadState.forumPostReadState.id.post)
|
||||
.from(QForumPostReadState.forumPostReadState)
|
||||
.where(
|
||||
QForumPostReadState.forumPostReadState.read.isTrue(),
|
||||
QForumPostReadState.forumPostReadState.id.user.eq(user)
|
||||
)
|
||||
),
|
||||
QForumThread.forumThread.in(forumThreadList)
|
||||
)
|
||||
.fetchOne();
|
||||
}
|
||||
}
|
||||
|
@ -23,5 +23,5 @@ public interface ProjectForumService {
|
||||
// TODO: Get these away from here
|
||||
List<Pair<ProjectThread, ForumPost>> latestPost(Project a, int amount);
|
||||
|
||||
boolean hasUnreadThreads(Project project, User user);
|
||||
long getUnreadThreadsCount(Project project, User user);
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ package se.su.dsv.scipro.forum;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import se.su.dsv.scipro.file.FileSource;
|
||||
import se.su.dsv.scipro.file.ProjectFileService;
|
||||
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
|
||||
@ -114,14 +115,10 @@ public class ProjectForumServiceImpl implements ProjectForumService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasUnreadThreads(Project project, User user) {
|
||||
public long getUnreadThreadsCount(Project project, User user) {
|
||||
List<ProjectThread> threads = getThreads(project);
|
||||
for (ProjectThread thread : threads) {
|
||||
if (!basicForumService.isThreadRead(user, thread.getForumThread())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
List<ForumThread> list = threads.stream().map(ProjectThread::getForumThread).toList();
|
||||
return basicForumService.countUnreadThreads(list, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS `grade`;
|
@ -105,9 +105,9 @@ public class ProjectForumServiceImplTest extends ForumModuleTest {
|
||||
final ProjectThread thread = service.createThread(project, supervisor, "subject", "content", Set.of());
|
||||
service.createReply(thread, author, "reply", Set.of());
|
||||
|
||||
boolean hasUnreadThreads = service.hasUnreadThreads(project, supervisor);
|
||||
long count = service.getUnreadThreadsCount(project, supervisor);
|
||||
|
||||
assertTrue(hasUnreadThreads);
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
private void assertNewForumThread(
|
||||
|
18
owasp.xml
18
owasp.xml
@ -72,4 +72,22 @@
|
||||
</notes>
|
||||
<cve>CVE-2024-23076</cve>
|
||||
</suppress>
|
||||
<suppress>
|
||||
<notes>
|
||||
https://nvd.nist.gov/vuln/detail/CVE-2024-49203
|
||||
https://github.com/querydsl/querydsl/issues/3757
|
||||
|
||||
Basically if you allow untrusted user input to be used in the "ORDER BY" clause
|
||||
you can be vulnerable to SQL injection.
|
||||
|
||||
I believe this is nonsense and akin to saying every Java application has a
|
||||
security vulnerability because JDBC allows you to execute arbitrary SQL if you
|
||||
do not properly use PreparedStatement with parameters over a string-concatenated
|
||||
Statement.
|
||||
|
||||
Even if this is considered a valid vulnerability we do not, currently, allow
|
||||
untrusted user input to be used in the "ORDER BY" clause.
|
||||
</notes>
|
||||
<cve>CVE-2024-49203</cve>
|
||||
</suppress>
|
||||
</suppressions>
|
||||
|
@ -6,9 +6,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<wicket:panel>
|
||||
<a wicket:id="toggle" href="#">
|
||||
<span wicket:id="icon" class="fa fa-flag read-state"></span>
|
||||
</a>
|
||||
<a wicket:id="toggle" href="#"><span wicket:id="icon" class="fa fa-flag read-state"></span></a>
|
||||
</wicket:panel>
|
||||
</body>
|
||||
</html>
|
@ -10,10 +10,14 @@ import org.apache.wicket.markup.html.panel.Panel;
|
||||
|
||||
public abstract class AbstractReadStatePanel extends Panel {
|
||||
|
||||
private final Component icon;
|
||||
public static final String TOGGLE = "toggle";
|
||||
static final String ICON = "icon";
|
||||
|
||||
public AbstractReadStatePanel(final String id) {
|
||||
super(id);
|
||||
Component icon = new UpdatingImage(ICON);
|
||||
icon.setOutputMarkupId(true);
|
||||
|
||||
AjaxFallbackLink<Void> link = new AjaxFallbackLink<>(TOGGLE) {
|
||||
@Override
|
||||
public void onClick(final Optional<AjaxRequestTarget> target) {
|
||||
@ -23,20 +27,15 @@ public abstract class AbstractReadStatePanel extends Panel {
|
||||
});
|
||||
}
|
||||
};
|
||||
add(link);
|
||||
|
||||
icon = new UpdatingImage(ICON);
|
||||
icon.setOutputMarkupId(true);
|
||||
link.add(icon);
|
||||
|
||||
add(link);
|
||||
}
|
||||
|
||||
protected abstract boolean isRead();
|
||||
|
||||
protected abstract void onFlagClick(final AjaxRequestTarget target);
|
||||
|
||||
public static final String TOGGLE = "toggle";
|
||||
static final String ICON = "icon";
|
||||
|
||||
private class UpdatingImage extends WebComponent {
|
||||
|
||||
public UpdatingImage(String id) {
|
||||
|
@ -41,6 +41,13 @@
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-hover" wicket:id="dp"></table>
|
||||
|
||||
<wicket:fragment wicket:id="readStateColumnMarkupId">
|
||||
<span wicket:id="flag"></span>
|
||||
<wicket:enclosure child="counter">
|
||||
(<wicket:container wicket:id="counter"></wicket:container>)
|
||||
</wicket:enclosure>
|
||||
</wicket:fragment>
|
||||
</wicket:panel>
|
||||
</body>
|
||||
</html>
|
@ -5,6 +5,7 @@ import static java.util.Arrays.asList;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.wicket.AttributeModifier;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
|
||||
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
|
||||
@ -14,16 +15,22 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColu
|
||||
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.data.table.LambdaColumn;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
|
||||
import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.markup.html.form.LambdaChoiceRenderer;
|
||||
import org.apache.wicket.markup.html.panel.Fragment;
|
||||
import org.apache.wicket.markup.html.panel.Panel;
|
||||
import org.apache.wicket.markup.repeater.Item;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LambdaModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import se.su.dsv.scipro.components.*;
|
||||
import se.su.dsv.scipro.components.AjaxCheckBoxMultipleChoice;
|
||||
import se.su.dsv.scipro.components.BootstrapRadioChoice;
|
||||
import se.su.dsv.scipro.components.ExportableDataPanel;
|
||||
import se.su.dsv.scipro.components.ListAdapterModel;
|
||||
import se.su.dsv.scipro.components.TemporalColumn;
|
||||
import se.su.dsv.scipro.components.datatables.MultipleUsersColumn;
|
||||
import se.su.dsv.scipro.components.datatables.UserColumn;
|
||||
import se.su.dsv.scipro.dataproviders.FilteredDataProvider;
|
||||
@ -250,25 +257,49 @@ public class SupervisorMyProjectsPanel extends Panel {
|
||||
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<Project>> item, String id, IModel<Project> projectModel) {
|
||||
item.add(
|
||||
new AbstractReadStatePanel(id) {
|
||||
@Override
|
||||
protected boolean isRead() {
|
||||
return !projectForumService.hasUnreadThreads(
|
||||
projectModel.getObject(),
|
||||
SciProSession.get().getUser()
|
||||
);
|
||||
}
|
||||
// Since table cell only can contain one item, we use Wicket Fragment here. It will contain two components,
|
||||
// one for flag, one for unread messages counter.
|
||||
|
||||
@Override
|
||||
protected void onFlagClick(AjaxRequestTarget target) {
|
||||
setResponsePage(
|
||||
SupervisorThreadedForumPage.class,
|
||||
SupervisorThreadedForumPage.getPageParameters(projectModel.getObject())
|
||||
);
|
||||
}
|
||||
}
|
||||
Fragment fragment = new Fragment(id, "readStateColumnMarkupId", SupervisorMyProjectsPanel.this);
|
||||
|
||||
long msgCount = projectForumService.getUnreadThreadsCount(
|
||||
projectModel.getObject(),
|
||||
SciProSession.get().getUser()
|
||||
);
|
||||
boolean isRead = msgCount == 0;
|
||||
|
||||
AbstractReadStatePanel readStatePanel = new AbstractReadStatePanel("flag") {
|
||||
@Override
|
||||
protected boolean isRead() {
|
||||
return isRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFlagClick(AjaxRequestTarget target) {
|
||||
setResponsePage(
|
||||
SupervisorThreadedForumPage.class,
|
||||
SupervisorThreadedForumPage.getPageParameters(projectModel.getObject())
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isRead) {
|
||||
readStatePanel.add(new AttributeModifier("title", getString("unread.msg")));
|
||||
}
|
||||
|
||||
fragment.add(readStatePanel);
|
||||
|
||||
Label counterLabel = new Label("counter", msgCount) {
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
setVisible(msgCount > 0);
|
||||
}
|
||||
};
|
||||
|
||||
fragment.add(counterLabel);
|
||||
|
||||
item.add(fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,3 +12,5 @@ ProjectStatus.COMPLETED= Completed
|
||||
|
||||
SupervisorProjectNoteDisplay.COMPACT=Compact
|
||||
SupervisorProjectNoteDisplay.FULL=Full
|
||||
|
||||
unread.msg=There are unread messages.
|
72
war/pom.xml
72
war/pom.xml
@ -12,6 +12,10 @@
|
||||
<artifactId>war</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<spring.profile.active>tomcat</spring.profile.active>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -88,7 +92,26 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<configuration>
|
||||
<propertiesEncoding>${project.build.sourceEncoding}</propertiesEncoding>
|
||||
<delimiters>
|
||||
<!-- delimiter for resource filtering is changed since Spring hijacks ${...} -->
|
||||
<delimiter>@</delimiter>
|
||||
</delimiters>
|
||||
<useDefaultDelimiters>false</useDefaultDelimiters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@ -110,4 +133,53 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>branch</id>
|
||||
<properties>
|
||||
<spring.profile.active>branch</spring.profile.active>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>docker-dependencies</id>
|
||||
<!--
|
||||
Some dependencies are not discovered by default when running dependency:go-offline.
|
||||
They are added here manually to allow Docker build layers to be cached properly.
|
||||
-->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml</groupId>
|
||||
<artifactId>classmate</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
<classifier>jakarta</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
@ -16,12 +16,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.orm.jpa.SharedEntityManagerCreator;
|
||||
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
|
||||
import org.springframework.web.filter.ForwardedHeaderFilter;
|
||||
import se.su.dsv.scipro.CoreConfig;
|
||||
import se.su.dsv.scipro.FileSystemStore;
|
||||
import se.su.dsv.scipro.RepositoryConfiguration;
|
||||
@ -84,6 +87,22 @@ public class Main extends SpringBootServletInitializer implements ServletContain
|
||||
return currentProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring runs on HTTP and is protected by a HTTPS proxy.
|
||||
* This filter takes the `X-Forwarded-*` headers and updates the request to reflect the original HTTP request.
|
||||
* <p>
|
||||
* Note: This is not needed when we're running behind Apache as a proxy since it uses an AJP connector that has a
|
||||
* built-in mechanism for handling this,
|
||||
* see <a href="https://tomcat.apache.org/connectors-doc/common_howto/proxy.html#AJP_as_a_Solution">AJP proxy documentation</a>.
|
||||
* So this is only for the temporary test servers that are running behind Traefik and use regular HTTP.
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
|
||||
var filterRegistrationBean = new FilterRegistrationBean<>(new ForwardedHeaderFilter());
|
||||
filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FileStore fileStore() {
|
||||
return new FileSystemStore();
|
||||
|
19
war/src/main/resources/application-branch.properties
Normal file
19
war/src/main/resources/application-branch.properties
Normal file
@ -0,0 +1,19 @@
|
||||
spring.datasource.url=${JDBC_DATABASE_URL}
|
||||
spring.datasource.username=${JDBC_DATABASE_USERNAME}
|
||||
spring.datasource.password=${JDBC_DATABASE_PASSWORD}
|
||||
|
||||
profile=DEV
|
||||
|
||||
# No secrets available for branch deployment to branch.dsv.su.se
|
||||
# Will have to set up some mock API for this later
|
||||
service.grading.url=
|
||||
oauth.uri=
|
||||
oauth.clientId=
|
||||
oauth.clientSecret=
|
||||
oauth.redirectUri=
|
||||
|
||||
# No secrets available for branch deployment to branch.dsv.su.se
|
||||
# Will have to set up some mock API for this later
|
||||
daisy.api.url=
|
||||
daisy.api.username=
|
||||
daisy.api.password=
|
1
war/src/main/resources/application-tomcat.properties
Normal file
1
war/src/main/resources/application-tomcat.properties
Normal file
@ -0,0 +1 @@
|
||||
spring.datasource.jndi-name=java:/comp/env/jdbc/sciproDS
|
@ -1,4 +1,4 @@
|
||||
spring.datasource.jndi-name=java:/comp/env/jdbc/sciproDS
|
||||
spring.profiles.active=@spring.profile.active@
|
||||
spring.flyway.baseline-version=2
|
||||
spring.flyway.baseline-on-migrate=true
|
||||
|
||||
@ -16,17 +16,17 @@ springdoc.swagger-ui.path=/swagger
|
||||
springdoc.swagger-ui.persist-authorization=true
|
||||
|
||||
# These will be overwritten by configuration in the environment of servers it is deployed to
|
||||
spring.security.oauth2.resourceserver.opaquetoken.client-id=scipro-api-client
|
||||
spring.security.oauth2.resourceserver.opaquetoken.client-secret=scipro-api-secret
|
||||
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:59733/introspect
|
||||
spring.security.oauth2.resourceserver.opaquetoken.client-id=${OAUTH2_RESOURCE_SERVER_ID:scipro-api-client}
|
||||
spring.security.oauth2.resourceserver.opaquetoken.client-secret=${OAUTH2_RESOURCE_SERVER_SECRET:scipro-api-secret}
|
||||
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=${OAUTH2_RESOURCE_SERVER_INTROSPECTION_URI:http://localhost:59733/introspect}
|
||||
|
||||
# Log in via local OAuth 2 authorization server
|
||||
spring.security.oauth2.client.provider.docker.user-info-uri=http://localhost:59734/verify
|
||||
spring.security.oauth2.client.provider.docker.user-info-uri=${OAUTH2_USER_INFO_URI:http://localhost:59734/verify}
|
||||
spring.security.oauth2.client.provider.docker.user-name-attribute=sub
|
||||
spring.security.oauth2.client.provider.docker.token-uri=http://localhost:59734/exchange
|
||||
spring.security.oauth2.client.provider.docker.authorization-uri=http://localhost:59734/authorize
|
||||
spring.security.oauth2.client.provider.docker.token-uri=${OAUTH2_TOKEN_URI:http://localhost:59734/exchange}
|
||||
spring.security.oauth2.client.provider.docker.authorization-uri=${OAUTH2_AUTHORIZATION_URI:http://localhost:59734/authorize}
|
||||
spring.security.oauth2.client.registration.scipro.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
|
||||
spring.security.oauth2.client.registration.scipro.provider=docker
|
||||
spring.security.oauth2.client.registration.scipro.client-id=scipro
|
||||
spring.security.oauth2.client.registration.scipro.client-secret=s3cr3t
|
||||
spring.security.oauth2.client.registration.scipro.client-id=${OAUTH2_CLIENT_ID:scipro}
|
||||
spring.security.oauth2.client.registration.scipro.client-secret=${OAUTH2_CLIENT_SECRET:s3cr3t}
|
||||
spring.security.oauth2.client.registration.scipro.authorization-grant-type=authorization_code
|
||||
|
Loading…
x
Reference in New Issue
Block a user