diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..f5eabdc
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,11 @@
+
+```shell
+# Генерация RSA ключа (2048 или 4096 бит)
+openssl genrsa -out private.key 2048
+
+# Конвертация в PEM?
+
+# Извлечение публичного ключа
+openssl rsa -in private.key -pubout -out public.key
+
+```
\ No newline at end of file
diff --git a/debug/application.yaml b/debug/application.yaml
index a056c55..418d09a 100644
--- a/debug/application.yaml
+++ b/debug/application.yaml
@@ -2,4 +2,4 @@ spring:
datasource:
url: jdbc:postgresql://localhost:5432/lti
username: postgres
- password: postgres
\ No newline at end of file
+ password: oodbpasswd
\ No newline at end of file
diff --git a/debug/docker-compose.without_pg.yaml b/debug/docker-compose.without_pg.yaml
new file mode 100644
index 0000000..c95e080
--- /dev/null
+++ b/debug/docker-compose.without_pg.yaml
@@ -0,0 +1,36 @@
+services:
+
+ zookeeper:
+ image: confluentinc/cp-zookeeper:7.6.1
+ container_name: zookeeper
+ environment:
+ ZOOKEEPER_CLIENT_PORT: 2181
+ ZOOKEEPER_TICK_TIME: 2000
+ restart: unless-stopped
+
+ kafka:
+ image: confluentinc/cp-kafka:7.6.1
+ container_name: kafka
+ depends_on:
+ - zookeeper
+ ports:
+ - "29092:29092"
+ environment:
+ KAFKA_BROKER_ID: 1
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
+ KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
+ restart: unless-stopped
+
+volumes:
+ postgres_data:
+
+networks:
+ lti-net:
+ driver: bridge
+ default:
+ name: lti-net
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 127ba4f..6f2a32b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,6 +33,11 @@
spring-boot-starter-data-jpa
+
+ org.springframework.security
+ spring-security-oauth2-jose
+
+
org.projectlombok
lombok
@@ -58,10 +63,20 @@
+
org.springframework.boot
spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
diff --git a/src/main/java/ru/oa2/lti/ApplicationService.java b/src/main/java/ru/oa2/lti/ApplicationService.java
new file mode 100644
index 0000000..1397d5f
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/ApplicationService.java
@@ -0,0 +1,42 @@
+package ru.oa2.lti;
+
+import jakarta.transaction.Transactional;
+import org.springframework.stereotype.Service;
+import ru.oa2.lti.model.LtiLogin;
+import ru.oa2.lti.repository.LMSContentRepository;
+import ru.oa2.lti.repository.entities.LMSContent;
+import ru.oa2.lti.repository.entities.Task;
+import ru.oa2.lti.service.jwt.JwtService;
+
+import java.util.Collection;
+
+@Service
+@Transactional
+public class ApplicationService {
+
+ private final JwtService jwtService;
+ private final LMSContentRepository lmsContentRepository;
+
+ public ApplicationService(JwtService jwtService,
+ LMSContentRepository lmsContentRepository) {
+ this.jwtService = jwtService;
+ this.lmsContentRepository = lmsContentRepository;
+ }
+
+ public String getTask(LtiLogin ltiLogin) {
+ var payload = jwtService.getPayload(ltiLogin.getLoginHint());
+
+ var content = lmsContentRepository.getLMSContentByContentId(payload.getContextId());
+
+ if(content.isPresent()) {
+ LMSContent lmsContent = content.get();
+ Collection tasks = lmsContent.getTasks();
+
+ //TODO добавить версию в Task и выбирать самую старшую и опубликованную
+ Object data = tasks.stream().findFirst().get().getData();
+ return (String)data;
+ } else {
+ return "Not Page";
+ }
+ }
+}
diff --git a/src/main/java/ru/oa2/lti/LTIProviderApplication.java b/src/main/java/ru/oa2/lti/LTIProviderApplication.java
index a06dc27..d23020f 100644
--- a/src/main/java/ru/oa2/lti/LTIProviderApplication.java
+++ b/src/main/java/ru/oa2/lti/LTIProviderApplication.java
@@ -2,8 +2,10 @@ package ru.oa2.lti;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
+@EnableTransactionManagement
public class LTIProviderApplication {
public static void main(String[] args) {
diff --git a/src/main/java/ru/oa2/lti/config/JwtConfig.java b/src/main/java/ru/oa2/lti/config/JwtConfig.java
new file mode 100644
index 0000000..15ca8d9
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/config/JwtConfig.java
@@ -0,0 +1,18 @@
+package ru.oa2.lti.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
+
+
+@Configuration
+public class JwtConfig {
+
+ @Bean
+ public JwtDecoder jwtDecoder() {
+ return NimbusJwtDecoder
+ .withJwkSetUri("http://openolat.local/lti/keys") //TODO адрес из application
+ .build();
+ }
+}
diff --git a/src/main/java/ru/oa2/lti/controller/LoginController.java b/src/main/java/ru/oa2/lti/controller/LoginController.java
index 1f9f44c..98405c6 100644
--- a/src/main/java/ru/oa2/lti/controller/LoginController.java
+++ b/src/main/java/ru/oa2/lti/controller/LoginController.java
@@ -1,7 +1,9 @@
package ru.oa2.lti.controller;
+import jakarta.servlet.ServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
+import ru.oa2.lti.ApplicationService;
import ru.oa2.lti.model.LtiLogin;
@Slf4j
@@ -9,6 +11,12 @@ import ru.oa2.lti.model.LtiLogin;
@RequestMapping("/tool/lti")
public class LoginController {
+ private final ApplicationService service;
+
+ public LoginController(ApplicationService applicationService) {
+ this.service = applicationService;
+ }
+
@PostMapping("/login")
public String login(
@RequestParam("client_id") String clientId,
@@ -16,8 +24,8 @@ public class LoginController {
@RequestParam("login_hint") String loginHint,
@RequestParam("lti_deployment_id") String ltiDeploymentId,
@RequestParam("lti_message_hint") String ltiMessageHint,
- @RequestParam("target_link_uri") String targetLinkUri
- ) {
+ @RequestParam("target_link_uri") String targetLinkUri,
+ ServletRequest servletRequest) {
var ltiLogin = LtiLogin.builder()
.clientId(clientId)
.iss(iss)
@@ -29,7 +37,6 @@ public class LoginController {
log.info("BODY: {}", ltiLogin);
- //TODO service get context (by student/teatcher)
- return "redirect:" + "/page1";
+ return service.getTask(ltiLogin);
}
}
diff --git a/src/main/java/ru/oa2/lti/controller/TODO.md b/src/main/java/ru/oa2/lti/controller/TODO.md
index ef6474f..ceac1d6 100644
--- a/src/main/java/ru/oa2/lti/controller/TODO.md
+++ b/src/main/java/ru/oa2/lti/controller/TODO.md
@@ -1,5 +1,5 @@
```shell
-curl -XPOST http://localhost:8080/tool/lti/login \
+curl -XPOST http://localhost:9999/tool/lti/login \
-d 'client_id=27e2d42d-d218-4ab9-b063-85e3ec87ec8f&iss=https%3A%2F%2Flocalhost&login_hint=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImU5ODA3YTg2LTVhNWQtNDI0MC1hYjI1LWNhYTUyZDliZjUxOSJ9.eyJkZXBsb3ltZW50S2V5IjoxLCJkZXBsb3ltZW50SWQiOiIyNzE1ZDA0NS03N2M1LTRkODgtYTFlNC05MmQ3MjFmY2M1NGIiLCJjb250ZXh0S2V5IjoxLCJjb250ZXh0SWQiOiJlMmE4ZGQxMy04MWJhLTQwMTEtODgwNi0wMjA5NGZkZjg5ZjMiLCJjb3Vyc2VhZG1pbiI6ZmFsc2UsImNvYWNoIjpmYWxzZSwicGFydGljaXBhbnQiOnRydWV9.vgPLHQA9scdED3_OwOy46h6VmzpN4arIHY3-YDBdhH4kuEqeCOjjtFbGdDqauKoQBTSVo4UvoK4JQLMhak6qsFchCj54mPob8jbaKLd0GnO_jY0sR609Nrk7Muq7cki_4PjVMX8TTHp-VYlSHjVxQH_z_D5Wld27J95z4qJjRU59GmvLGDqdLyerVVBO-zaavYsUbEEiAxoX3hmytxrarmJ7OHpxufNOeXzZ0DSGUmU5ycuTAqxODaHO1Y4rQM6XlvSfDh_TmXP8QEkatlp2cdjRpNWyOdUW_hZfbtkqukwt1ZP7KEgWzNI3vivpBjm2xfUG2YLwXPJqHa47NQgvsQ<i_deployment_id=2715d045-77c5-4d88-a1e4-92d721fcc54b<i_message_hint=1802240&target_link_uri=https%3A%2F%2Fopenolat.local%2Ftool'
```
\ No newline at end of file
diff --git a/src/main/java/ru/oa2/lti/domain/carecase/GetTask.java b/src/main/java/ru/oa2/lti/domain/carecase/GetTask.java
new file mode 100644
index 0000000..846d44f
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/domain/carecase/GetTask.java
@@ -0,0 +1,5 @@
+package ru.oa2.lti.domain.carecase;
+
+public class GetTask {
+
+}
diff --git a/src/main/java/ru/oa2/lti/repository/LMSContentRepository.java b/src/main/java/ru/oa2/lti/repository/LMSContentRepository.java
new file mode 100644
index 0000000..4331f13
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/repository/LMSContentRepository.java
@@ -0,0 +1,16 @@
+package ru.oa2.lti.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import ru.oa2.lti.repository.entities.LMSContent;
+
+import java.util.Optional;
+import java.util.UUID;
+
+@Repository
+public interface LMSContentRepository extends JpaRepository {
+
+ Optional getLMSContentByContentKey(long id);
+ Optional getLMSContentByContentId(UUID contentId);
+ Optional findByContentId(UUID contentId);
+}
diff --git a/src/main/java/ru/oa2/lti/repository/TaskRepository.java b/src/main/java/ru/oa2/lti/repository/TaskRepository.java
deleted file mode 100644
index 4e674af..0000000
--- a/src/main/java/ru/oa2/lti/repository/TaskRepository.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package ru.oa2.lti.repository;
-
-public interface TaskRepository {
-}
diff --git a/src/main/java/ru/oa2/lti/repository/entities/LMSContent.java b/src/main/java/ru/oa2/lti/repository/entities/LMSContent.java
new file mode 100644
index 0000000..ce8bd84
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/repository/entities/LMSContent.java
@@ -0,0 +1,29 @@
+package ru.oa2.lti.repository.entities;
+
+import jakarta.persistence.*;
+import lombok.Data;
+
+import java.util.Collection;
+import java.util.UUID;
+
+@Data
+@Entity
+@Table(name = "lms_content")
+public class LMSContent {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ long id;
+
+ @Column(name = "content_key")
+ long contentKey;
+
+ @Column(name = "content_uuid")
+ UUID contentId;
+
+ @Column(name = "deployment_id")
+ UUID deploymentId;
+
+ @OneToMany(mappedBy="id", fetch = FetchType.LAZY)
+ Collection tasks;
+}
diff --git a/src/main/java/ru/oa2/lti/repository/entities/Task.java b/src/main/java/ru/oa2/lti/repository/entities/Task.java
index cebbd83..ff214c5 100644
--- a/src/main/java/ru/oa2/lti/repository/entities/Task.java
+++ b/src/main/java/ru/oa2/lti/repository/entities/Task.java
@@ -1,20 +1,22 @@
package ru.oa2.lti.repository.entities;
import jakarta.persistence.*;
+import lombok.Data;
import ru.oa2.lti.model.TaskType;
+@Data
@Entity
@Table(name = "task")
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
- long taskId;
+ long id;
- @Column(length = 20)
+ @Column(name = "task_type", length = 20)
@Enumerated(EnumType.STRING)
TaskType taskType;
- @Column(columnDefinition = "jsonb")
+ @Column(name = "data", columnDefinition = "jsonb")
Object data;
}
diff --git a/src/main/java/ru/oa2/lti/service/jwt/JwtService.java b/src/main/java/ru/oa2/lti/service/jwt/JwtService.java
new file mode 100644
index 0000000..97e4751
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/service/jwt/JwtService.java
@@ -0,0 +1,6 @@
+package ru.oa2.lti.service.jwt;
+
+public interface JwtService {
+
+ Payload getPayload(String jwt);
+}
diff --git a/src/main/java/ru/oa2/lti/service/jwt/JwtServiceImpl.java b/src/main/java/ru/oa2/lti/service/jwt/JwtServiceImpl.java
new file mode 100644
index 0000000..3de1886
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/service/jwt/JwtServiceImpl.java
@@ -0,0 +1,31 @@
+package ru.oa2.lti.service.jwt;
+
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+@Service
+public class JwtServiceImpl implements JwtService {
+
+ private JwtDecoder jwtDecoder;
+
+ public JwtServiceImpl(JwtDecoder jwtDecoder) {
+ this.jwtDecoder = jwtDecoder;
+ }
+
+ @Override
+ public Payload getPayload(String token) {
+ var jwt = jwtDecoder.decode(token);
+
+ return Payload.builder()
+ .deploymentKey(jwt.getClaim("deploymentKey"))
+ .deploymentId(UUID.fromString(jwt.getClaim("deploymentId")))
+ .contextKey(jwt.getClaim("contextKey"))
+ .contextId(UUID.fromString(jwt.getClaim("contextId")))
+ .courseadmin(jwt.getClaim("courseadmin"))
+ .coach(jwt.getClaim("coach"))
+ .participant(jwt.getClaim("participant"))
+ .build();
+ }
+}
diff --git a/src/main/java/ru/oa2/lti/service/jwt/Payload.java b/src/main/java/ru/oa2/lti/service/jwt/Payload.java
new file mode 100644
index 0000000..78cc27c
--- /dev/null
+++ b/src/main/java/ru/oa2/lti/service/jwt/Payload.java
@@ -0,0 +1,18 @@
+package ru.oa2.lti.service.jwt;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.UUID;
+
+@Data
+@Builder
+public class Payload {
+ private Long deploymentKey;
+ private UUID deploymentId;
+ private Long contextKey;
+ private UUID contextId;
+ private Boolean courseadmin;
+ private Boolean coach;
+ private Boolean participant;
+}
diff --git a/src/main/java/ru/oa2/lti/service/TaskService.java b/src/main/java/ru/oa2/lti/service/task/TaskService.java
similarity index 95%
rename from src/main/java/ru/oa2/lti/service/TaskService.java
rename to src/main/java/ru/oa2/lti/service/task/TaskService.java
index 3253596..da0b909 100644
--- a/src/main/java/ru/oa2/lti/service/TaskService.java
+++ b/src/main/java/ru/oa2/lti/service/task/TaskService.java
@@ -1,4 +1,4 @@
-package ru.oa2.lti.service;
+package ru.oa2.lti.service.task;
import ru.oa2.lti.model.ResultRequest;
import ru.oa2.lti.model.TaskResult;
diff --git a/src/main/java/ru/oa2/lti/service/TaskServiceImpl.java b/src/main/java/ru/oa2/lti/service/task/TaskServiceImpl.java
similarity index 93%
rename from src/main/java/ru/oa2/lti/service/TaskServiceImpl.java
rename to src/main/java/ru/oa2/lti/service/task/TaskServiceImpl.java
index ed0e9f5..c36db20 100644
--- a/src/main/java/ru/oa2/lti/service/TaskServiceImpl.java
+++ b/src/main/java/ru/oa2/lti/service/task/TaskServiceImpl.java
@@ -1,4 +1,4 @@
-package ru.oa2.lti.service;
+package ru.oa2.lti.service.task;
import ru.oa2.lti.model.ResultRequest;
import ru.oa2.lti.model.TaskResult;
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index dec1a39..186e2c1 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -1,5 +1,12 @@
spring:
+ jpa:
+ # Показывать SQL запросы
+ show-sql: true
+ properties:
+ hibernate:
+ format_sql: true
+
application:
name: lti-provider
@@ -12,4 +19,7 @@ spring:
server:
port: 9999
-
+logging:
+ level:
+ org.hibernate.SQL: DEBUG
+ org.hibernate.type.descriptor.sql.BasicBinder: TRACE
\ No newline at end of file
diff --git a/src/main/resources/db/changelog/1.0.0/2025-12-add_lms_content.xml b/src/main/resources/db/changelog/1.0.0/2025-12-add_lms_content.xml
new file mode 100644
index 0000000..70b82d7
--- /dev/null
+++ b/src/main/resources/db/changelog/1.0.0/2025-12-add_lms_content.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/db/changelog/1.0.0/2025-12-add_task.xml b/src/main/resources/db/changelog/1.0.0/2025-12-add_task.xml
index a4f0a7f..efef0d5 100644
--- a/src/main/resources/db/changelog/1.0.0/2025-12-add_task.xml
+++ b/src/main/resources/db/changelog/1.0.0/2025-12-add_task.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/src/main/resources/db/changelog/changelog-master.xml b/src/main/resources/db/changelog/changelog-master.xml
index 6902742..87a6231 100644
--- a/src/main/resources/db/changelog/changelog-master.xml
+++ b/src/main/resources/db/changelog/changelog-master.xml
@@ -9,5 +9,6 @@
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
+
\ No newline at end of file