Skip to content

Commit

Permalink
Merge branch 'develop' into chore/architecture-style
Browse files Browse the repository at this point in the history
  • Loading branch information
Strohgelaender authored Sep 9, 2023
2 parents ac44d54 + f6e9a37 commit 179202f
Show file tree
Hide file tree
Showing 61 changed files with 1,665 additions and 152 deletions.
47 changes: 1 addition & 46 deletions docs/dev/setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -645,51 +645,6 @@ HTTPS. We need to extend the Artemis configuration in the file

------------------------------------------------------------------------------------------------------------------------

Hermes Service
--------------

Push notifications for the mobile Android and iOS clients rely on the Hermes_ service.
To enable push notifications the Hermes service needs to be started separately and the configuration of the Artemis instance must be extended.

Configure and start Hermes
^^^^^^^^^^^^^^^^^^^^^^^^^^

To run Hermes, you need to clone the `repository <https://github.com/ls1intum/Hermes>`_ and replace the placeholders within the ``docker-compose`` file.

The following environment variables need to be updated for push notifications to Apple devices:

* ``APNS_CERTIFICATE_PATH``: String - Path to the APNs certificate .p12 file as described `here <https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_certificate-based_connection_to_apns>`_
* ``APNS_CERTIFICATE_PWD``: String - The APNS certificate password
* ``APNS_PROD_ENVIRONMENT``: Bool - True if it should use the Production APNS Server (Default false)

Furthermore, the <APNS_Key>.p12 needs to be mounted into the Docker under the above specified path.

To run the services for Android support the following environment variable is required:

* ``GOOGLE_APPLICATION_CREDENTIALS``: String - Path to the firebase.json

Furthermore, the Firebase.json needs to be mounted into the Docker under the above specified path.

To run both APNS and Firebase, configure the environment variables for both.

To start Hermes, run the ``docker compose up`` command in the folder where the ``docker-compose`` file is located.

Artemis Configuration
^^^^^^^^^^^^^^^^^^^^^

The Hermes service is running on a dedicated machine and is addressed via
HTTPS. We need to extend the Artemis configuration in the file
``src/main/resources/config/application-artemis.yml`` like:

.. code:: yaml
artemis:
# ...
push-notification-relay: <url>
.. _Hermes: https://github.com/ls1intum/Hermes

------------------------------------------------------------------------------------------------------------------------

Athena Service
--------------
Expand Down Expand Up @@ -802,7 +757,7 @@ Other Docker Compose Setups

.. figure:: setup/artemis-docker-file-structure.drawio.png
:align: center
:target: ../../_images/artemis-docker-file-structure.drawio.png
:target: ../_images/artemis-docker-file-structure.drawio.png

Overview of the Artemis Docker / Docker Compose structure

Expand Down
49 changes: 26 additions & 23 deletions docs/dev/setup/bamboo-bitbucket-jira.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,32 +134,32 @@ under ``localhost:7990``.
- **Jira:**
* - .. figure:: setup/bamboo-bitbucket-jira/bamboo_bitbucket_applicationLink.png
:align: center
:target: ../../_images/bamboo_bitbucket_applicationLink.png
:target: ../_images/bamboo_bitbucket_applicationLink.png

Bamboo → Bitbucket
- .. figure:: setup/bamboo-bitbucket-jira/bitbucket_bamboo_applicationLink.png
:align: center
:target: ../../_images/bitbucket_bamboo_applicationLink.png
:target: ../_images/bitbucket_bamboo_applicationLink.png

Bitbucket → Bamboo
- .. figure:: setup/bamboo-bitbucket-jira/jira_bamboo_applicationLink.png
:align: center
:target: ../../_images/jira_bamboo_applicationLink.png
:target: ../_images/jira_bamboo_applicationLink.png

Jira → Bamboo
* - .. figure:: setup/bamboo-bitbucket-jira/bamboo_jira_applicationLink.png
:align: center
:target: ../../_images/bamboo_jira_applicationLink.png
:target: ../_images/bamboo_jira_applicationLink.png

Bamboo → Jira
- .. figure:: setup/bamboo-bitbucket-jira/bitbucket_jira_applicationLink.png
:align: center
:target: ../../_images/bitbucket_jira_applicationLink.png
:target: ../_images/bitbucket_jira_applicationLink.png

Bitbucket → Jira
- .. figure:: setup/bamboo-bitbucket-jira/jira_bitbucket_applicationLink.png
:align: center
:target: ../../_images/jira_bitbucket_applicationLink.png
:target: ../_images/jira_bitbucket_applicationLink.png

Jira → Bitbucket

Expand All @@ -175,8 +175,8 @@ under ``localhost:7990``.
Jira <https://confluence.atlassian.com/adminjiraserver/allowing-connections-to-jira-for-user-management-938847045.html>`__
to synchronize the users in bitbucket and bamboo:

- Go to Jira → User management → Jira user server → Add application →
Create one application for bitbucket and one for bamboo → add the
- Go to `Jira → User management → Jira user server <http://localhost:8081/secure/admin/ConfigureCrowdServer.jspa>`__
→ Add application → Create one application for bitbucket and one for bamboo → add the
IP-address ``0.0.0.0/0`` to IP Addresses

.. list-table::
Expand All @@ -185,12 +185,12 @@ under ``localhost:7990``.

- .. figure:: setup/bamboo-bitbucket-jira/jira_add_application_bamboo.png

- Go to Bitbucket and Bamboo → User Directories → Add Directories →
Atlassian Crowd → use the URL ``http://jira:8080`` as Server URL →
- Go to `Bitbucket → User Directories <http://localhost:7990/plugins/servlet/embedded-crowd/directories/list>`__
and `Bamboo → User Directories <http://localhost:8085/plugins/servlet/embedded-crowd/directories/list>`__
→ Add Directories → Atlassian Crowd → use the URL ``http://jira:8080`` as Server URL →
use the application name and password which you used in the previous
step. Also, you should decrease the synchronisation period (e.g. to 2
minutes). Press synchronise after adding the directory, the users and
groups should now be available.
minutes).

.. list-table::

Expand All @@ -202,10 +202,12 @@ under ``localhost:7990``.

Adding Crowd Server in **Bamboo**

#. Give the test users User access on Bitbucket: On the Administration interface (settings cogwheel on the top),
go to the Global permissions. Type the names of all test users in the search field ("Add Users") and give them
the "Bitbucket User" permission. If you skip this step, the users will not be able to log in to Bitbucket or
clone repositories.
- Press synchronise after adding the directory, the users and groups should now be available.

#. Give the test users User access on Bitbucket: `On the Administration interface (settings cogwheel on the top),
go to the Global permissions <http://localhost:7990/admin/permissions>`__. Type the names of all test users in
the search field ("Add Users") and give them the "Bitbucket User" permission. If you skip this step, the users
will not be able to log in to Bitbucket or clone repositories.

#. In Bamboo create a global variable named
SERVER_PLUGIN_SECRET_PASSWORD, the value of this variable will be used
Expand All @@ -216,16 +218,17 @@ under ``localhost:7990``.

#. Download the
`bamboo-server-notification-plugin <https://github.com/ls1intum/bamboo-server-notification-plugin/releases>`__
and add it to bamboo. Go to Bamboo → Manage apps → Upload app → select
and add it to bamboo. Go to `Bamboo → Manage apps <http://localhost:8085/plugins/servlet/upm>`__ → Upload app → select
the downloaded .jar file → Upload

#. Authorize the Bamboo agent. Bamboo Administration → Agents → Remote agents → Agent authentication
#. Authorize the Bamboo agent. `Bamboo Administration → Agents <http://localhost:8085/admin/agent/configureAgents!doDefault.action>`__
→ Remote agents → Agent authentication

Approve the agent and edit the IP address in a development setup to ``*.*.*.*`` as the Docker container doesn't
have a static IP address.

.. figure:: setup/bamboo-bitbucket-jira/bamboo_agent_configuration.png
:target: ../../_images/bamboo_agent_configuration.png
:target: ../_images/bamboo_agent_configuration.png
:align: center

#. Generate a personal access token
Expand All @@ -235,8 +238,8 @@ under ``localhost:7990``.

#. Personal access token for Bamboo:

- Log in as the admin user and go to Bamboo → Profile (top right corner) → Personal access tokens →
Create token
- Log in as the admin user and go to `Bamboo → Profile (top right corner) → Personal access tokens →
Create token <http://localhost:8085/profile/createAccessToken.action>`__

.. figure:: setup/bamboo-bitbucket-jira/bamboo-create-token.png
:align: center
Expand All @@ -253,8 +256,8 @@ under ``localhost:7990``.
#. Personal access token for Bitbucket:

- Log in as the admin user and go to Bitbucket → Your profile image (top right corner) → Manage account
HTTP access tokens → Create token
- Log in as the admin user and go to `Bitbucket → Your profile image (top right corner) → Manage account
<http://localhost:7990/account>`__ → HTTP access tokens → Create token

.. figure:: setup/bamboo-bitbucket-jira/bitbucket_create_token.png
:align: center
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/Exercise.java
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,8 @@ public String getMappedColumnName() {

public abstract ExerciseType getExerciseType();

public abstract String getType();

/**
* Disconnects child entities from the exercise.
* <p>
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/exam/ExamSession.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package de.tum.in.www1.artemis.domain.exam;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

import org.hibernate.annotations.Cache;
Expand Down Expand Up @@ -40,6 +43,9 @@ public class ExamSession extends AbstractAuditingEntity {
@Transient
private boolean isInitialSessionTransient;

@Transient
private Set<SuspiciousSessionReason> suspiciousSessionReasons = new HashSet<>();

public StudentExam getStudentExam() {
return studentExam;
}
Expand Down Expand Up @@ -106,10 +112,35 @@ public void setInitialSession(boolean isInitialSessionTransient) {
this.isInitialSessionTransient = isInitialSessionTransient;
}

public Set<SuspiciousSessionReason> getSuspiciousReasons() {
return suspiciousSessionReasons;
}

public void setSuspiciousReasons(Set<SuspiciousSessionReason> suspiciousSessionReasons) {
this.suspiciousSessionReasons = suspiciousSessionReasons;
}

public void addSuspiciousReason(SuspiciousSessionReason suspiciousSessionReason) {
this.suspiciousSessionReasons.add(suspiciousSessionReason);
}

public void hideDetails() {
setUserAgent(null);
setBrowserFingerprintHash(null);
setInstanceId(null);
setIpAddress(null);
}

@JsonIgnore
public boolean hasSameIpAddress(ExamSession other) {

return other != null && getIpAddressAsIpAddress() != null && getIpAddressAsIpAddress().equals(other.getIpAddressAsIpAddress());
}

@JsonIgnore
public boolean hasSameBrowserFingerprint(ExamSession other) {

return other != null && getBrowserFingerprintHash() != null && getBrowserFingerprintHash().equals(other.getBrowserFingerprintHash());
}

}
13 changes: 13 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/exam/StudentExam.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,18 @@ public void setExamSessions(Set<ExamSession> examSessions) {
this.examSessions = examSessions;
}

/**
* Adds the given exam session to the student exam
*
* @param examSession the exam session to add
* @return the student exam with the added exam session
*/
public StudentExam addExamSession(ExamSession examSession) {
this.examSessions.add(examSession);
examSession.setStudentExam(this);
return this;
}

/**
* check if the individual student exam has ended (based on the working time)
* For test exams, we cannot use exam.startTime, but need to use the student.startedDate. If this is not yet set,
Expand Down Expand Up @@ -230,4 +242,5 @@ public boolean areResultsPublishedYet() {
return exam.resultsPublished();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.tum.in.www1.artemis.domain.exam;

import java.util.Set;

/**
* A set of related exam sessions that are suspicious.
* An exam session is suspicious if it shares the same browser fingerprint hash or user agent or IP address with another
* exam session that attempts a different student exam.
*
* @param examSessions the set of exam sessions that are suspicious
*/
public record SuspiciousExamSessions(Set<ExamSession> examSessions) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.tum.in.www1.artemis.domain.exam;

/**
* Enum representing reasons why a session is considered suspicious.
*/
public enum SuspiciousSessionReason {
SAME_IP_ADDRESS, SAME_BROWSER_FINGERPRINT
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.tum.in.www1.artemis.repository;

import java.util.Set;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
Expand All @@ -20,4 +22,42 @@ SELECT count(es.id)
""")
long findExamSessionCountByStudentExamId(@Param("studentExamId") Long studentExamId);

@Query("""
SELECT es
FROM ExamSession es
WHERE es.studentExam.exam.id = :examId
AND es.id <> :#{#examSession.id}
AND es.studentExam.id <> :#{#examSession.studentExam.id}
AND es.ipAddress = :#{#examSession.ipAddress}
AND es.browserFingerprintHash = :#{#examSession.browserFingerprintHash}
""")
Set<ExamSession> findAllExamSessionsWithTheSameIpAddressAndBrowserFingerprintByExamIdAndExamSession(long examId, @Param("examSession") ExamSession examSession);

@Query("""
SELECT es
FROM ExamSession es
WHERE es.studentExam.exam.id = :examId
""")
Set<ExamSession> findAllExamSessionsByExamId(long examId);

@Query("""
SELECT es
FROM ExamSession es
WHERE es.studentExam.exam.id = :examId
AND es.id <> :#{#examSession.id}
AND es.studentExam.id <> :#{#examSession.studentExam.id}
AND es.browserFingerprintHash = :#{#examSession.browserFingerprintHash}
""")
Set<ExamSession> findAllExamSessionsWithTheSameBrowserFingerprintByExamIdAndExamSession(long examId, @Param("examSession") ExamSession examSession);

@Query("""
SELECT es
FROM ExamSession es
WHERE es.studentExam.exam.id = :examId
AND es.id <> :#{#examSession.id}
AND es.studentExam.id <> :#{#examSession.studentExam.id}
AND es.ipAddress = :#{#examSession.ipAddress}
""")
Set<ExamSession> findAllExamSessionsWithTheSameIpAddressByExamIdAndExamSession(long examId, @Param("examSession") ExamSession examSession);

}
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,21 @@ default boolean toggleSecondCorrection(Exercise exercise) {
OR students.id = :userId
""")
Set<Exercise> getAllExercisesUserParticipatedInWithEagerParticipationsSubmissionsResultsFeedbacksByUserId(long userId);

/**
* For an explanation, see {@link de.tum.in.www1.artemis.web.rest.ExamResource#getAllExercisesWithPotentialPlagiarismForExam(long,long)}
*
* @param examId the id of the exam for which we want to get all exercises with potential plagiarism
* @return a list of exercises with potential plagiarism
*/
@Query("""
SELECT e
FROM Exercise e
LEFT JOIN e.exerciseGroup eg
WHERE eg IS NOT NULL
AND eg.exam.id = :examId
AND TYPE (e) IN (ModelingExercise, TextExercise, ProgrammingExercise)
""")
Set<Exercise> findAllExercisesWithPotentialPlagiarismByExamId(long examId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,18 @@ default PlagiarismCase findByIdWithPlagiarismSubmissionsElseThrow(long plagiaris
default PlagiarismCase findByIdElseThrow(long plagiarismCaseId) {
return findById(plagiarismCaseId).orElseThrow(() -> new EntityNotFoundException("PlagiarismCase", plagiarismCaseId));
}

/**
* Count the number of plagiarism cases for a given exercise id excluding deleted users.
*
* @param exerciseId the id of the exercise
* @return the number of plagiarism cases
*/
@Query("""
SELECT COUNT(plagiarismCase)
FROM PlagiarismCase plagiarismCase
WHERE plagiarismCase.student.isDeleted = false
AND plagiarismCase.exercise.id = :exerciseId
""")
long countByExerciseId(long exerciseId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,6 @@ default PlagiarismComparison<?> findByIdWithSubmissionsStudentsAndElementsBElseT
@Transactional // ok because of modifying query
@Query("UPDATE PlagiarismComparison plagiarismComparison set plagiarismComparison.status = :status where plagiarismComparison.id = :plagiarismComparisonId")
void updatePlagiarismComparisonStatus(@Param("plagiarismComparisonId") Long plagiarismComparisonId, @Param("status") PlagiarismStatus status);

Set<PlagiarismComparison<?>> findAllByPlagiarismResultExerciseId(long exerciseId);
}
Loading

0 comments on commit 179202f

Please sign in to comment.