Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication complete #5

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified data/QAData.mv.db
Binary file not shown.
2,771 changes: 247 additions & 2,524 deletions data/QAData.trace.db

Large diffs are not rendered by default.

17 changes: 14 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<version>3.0.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.serge.nsn</groupId>
Expand Down Expand Up @@ -80,8 +80,19 @@
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>

Expand Down
15 changes: 15 additions & 0 deletions src/main/kotlin/com/serge/nsn/qahub/QAHubApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.serge.nsn.qahub

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication

class QAHubApplication

fun main(args: Array<String>) {
runApplication<QAHubApplication>(*args)
}

//typealias Id = UUID
//val id = Id()
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.serge.nsn.qahub.api.controller

import com.serge.nsn.qahub.api.dto.UserDto
import com.serge.nsn.qahub.api.model.LoginRequest
import com.serge.nsn.qahub.api.model.LoginResponse
import com.serge.nsn.qahub.configuration.jwt.JWTService
import com.serge.nsn.qahub.services.UserService
import lombok.RequiredArgsConstructor
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
class jAuthenticationController(
private val jwtService: JWTService,
private val authenticationManager: AuthenticationManager,
private val userService: UserService
) {

@PostMapping("/register")
fun registerUser(@RequestBody dto: UserDto): ResponseEntity<UserDto> =
ResponseEntity(userService.register_user(dto), HttpStatus.CREATED)

@PostMapping("/login")
fun login(@RequestBody request: LoginRequest): LoginResponse {
val authentication = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken(request.username, request.password))
return if (authentication.isAuthenticated) {
LoginResponse(accessToken = jwtService.generateToken(request.username))
} else {
throw UsernameNotFoundException("Invalid user credentials")
}
}

@RequestMapping("/batch-registration")


@PostMapping
fun createUsers(@RequestBody newUsers: List<UserDto>): ResponseEntity<List<UserDto>> {
return ResponseEntity(userService.batchRegister(newUsers), HttpStatus.CREATED)

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.serge.nsn.qahub.api.controller

import com.serge.nsn.qahub.api.dto.PostDto
import com.serge.nsn.qahub.data.entities.PostType
import com.serge.nsn.qahub.services.PostService
import org.springframework.web.bind.annotation.*
import java.security.Principal

@RestController
@RequestMapping("/api/post")
class PostController(
private val postService: PostService,
) {

@GetMapping("/all")
fun getAll() = postService.getAllPosts()

//Delete post
@DeleteMapping("/{postId}/delete")
fun deletePost(@PathVariable postId: Long) = postService.deletePost(postId)


//Post Question
@CrossOrigin(origins = ["http://localhost:4200/"])
@PostMapping("/ask")
fun ask(@RequestBody question: PostDto, principal: Principal) = postService.askQuestion(question, principal)

//Get Question by Id
@GetMapping("/question/{qId}")
fun ask(@PathVariable qId: Long) = postService.getQuestionByID(qId)

//Get all questions
@GetMapping("/questions")
fun ask() = postService.getAllQuestions()

//Get Questions asked by user
@GetMapping("/questions/{uId}")
fun userQuestions(@PathVariable uId: Long) = postService.getPostByTypeAndUserId(PostType.QUESTION, uId)


//Get posts by user
@GetMapping("/posts/{uId}")
fun questionsByUser(@PathVariable uId: Long) = postService.allPostsByUserId(uId)


//Answer Question
@PostMapping("/{qId}/answer")
fun answer(@RequestBody answer: PostDto, @PathVariable qId: Long, principal: Principal) =
postService.answerQuestion(answer, principal, qId)


//All Answers to question
@GetMapping("/{qId}/answers")
fun answers(@PathVariable qId: Long) = postService.getAnswersByQuestionId(PostType.ANSWER, qId)


@PostMapping("/{postId}/comment")
fun reply(
@RequestBody comment: PostDto,
@PathVariable postId: Long,
principal: Principal
) =
postService.comment(comment, postId, principal)


//All comments under post
@GetMapping("/{postId}/comments")
fun comments(@PathVariable postId: Long) = postService.getAllComments(postId)

//upvote
@PostMapping("/{postId}/upvote")
fun upvote(@PathVariable postId: Long, principal: Principal) = postService.upVote(postId, principal)

//downvote
@PostMapping("/{postId}/downvote")
fun downvote(@PathVariable postId: Long, principal: Principal) = postService.downVote(postId, principal)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.serge.nsn.qahub.api.controller

import com.serge.nsn.qahub.api.dto.SubscriptionDto
import com.serge.nsn.qahub.services.SubscriptionService
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.security.Principal

@RestController
@RequestMapping("/api/subscribe")
class SubscriptionController(
private val subscriptionService: SubscriptionService
) {
@PostMapping("/new")
fun subscribeUser(@RequestBody subscriptionDto: SubscriptionDto, principal: Principal): SubscriptionDto {
return subscriptionService.subscribeUser(subscriptionDto, principal)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.serge.nsn.qahub.api.controller

import com.serge.nsn.qahub.api.dto.UserDto
import com.serge.nsn.qahub.services.UserService
import org.springframework.web.bind.annotation.*
import java.security.Principal

@RestController
@CrossOrigin
@RequestMapping("/api/user")
class UserController(
private val userService: UserService,
) {

@GetMapping("/all")
fun getUsers(): List<UserDto> = userService.getAll()

@GetMapping("/{userId}")
fun getUser(@PathVariable userId: Long) = userService.getById(userId)

@GetMapping("/user")
fun getCurrentUser(principal: Principal): String {
val username = principal.name
return "Current user: $username"
}

}
23 changes: 23 additions & 0 deletions src/main/kotlin/com/serge/nsn/qahub/api/dto/PostDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.serge.nsn.qahub.api.dto

import com.serge.nsn.qahub.data.entities.PostEntity

class PostDto(
val id: Long? = null,
val title: String? = "",
val content: String = "",
val author: String = "",
val tag: String?


) {
constructor(postEntity: PostEntity) : this(
id = postEntity.id,
title = postEntity.title,
content = postEntity.content,
author = postEntity.user.name,
tag = postEntity.tag,


)
}
20 changes: 20 additions & 0 deletions src/main/kotlin/com/serge/nsn/qahub/api/dto/SubscriptionDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.serge.nsn.qahub.api.dto

import com.serge.nsn.qahub.data.entities.Subscription
import java.time.LocalDate

class SubscriptionDto(
val id: Long = 0,
val user: String?,
val plan: String,
val startDate: LocalDate?,
val endDate: LocalDate?
) {
constructor(subscription: Subscription) : this(
id = subscription.id,
user = subscription.user.username,
plan = subscription.plan.name,
startDate = subscription.startDate,
endDate = subscription.endDate
)
}
22 changes: 22 additions & 0 deletions src/main/kotlin/com/serge/nsn/qahub/api/dto/UserDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.serge.nsn.qahub.api.dto

import com.serge.nsn.qahub.data.entities.UserEntity

class UserDto(
val user_id: Long? = null,
val name: String = "",
val username: String = "",
val password: String = "",
val email: String = "",
val role: String = "",

) {
constructor(userEntity: UserEntity) : this(
user_id = userEntity.id,
name = userEntity.name,
username = userEntity.username,
email = userEntity.email,
role = userEntity.roles,
// questions = userEntity.questions.map { QuestionDto(it) }
)
}
6 changes: 6 additions & 0 deletions src/main/kotlin/com/serge/nsn/qahub/api/model/LoginRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.serge.nsn.qahub.api.model

class LoginRequest(
val username: String,
val password: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.serge.nsn.qahub.api.model

data class LoginResponse(val accessToken: String)
56 changes: 56 additions & 0 deletions src/main/kotlin/com/serge/nsn/qahub/configuration/QAUserDetails.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.serge.nsn.qahub.configuration

import com.serge.nsn.qahub.data.entities.UserEntity
import lombok.Data
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import java.util.*
import java.util.stream.Collectors


@Data
class QAUserDetails(user: UserEntity) : UserDetails {
private val userName: String
private val password: String
private val authorities: List<GrantedAuthority>

init {
userName = user.email
password = user.password
authorities = Arrays.stream(
user.roles
.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
)
.map { role: String? -> SimpleGrantedAuthority(role) }
.collect(Collectors.toList())
}

override fun getAuthorities(): Collection<GrantedAuthority> {
return authorities
}

override fun getPassword(): String {
return password
}

override fun getUsername(): String {
return userName
}

override fun isAccountNonExpired(): Boolean {
return true
}

override fun isAccountNonLocked(): Boolean {
return true
}

override fun isCredentialsNonExpired(): Boolean {
return true
}

override fun isEnabled(): Boolean {
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.serge.nsn.qahub.configuration

import com.serge.nsn.qahub.data.repositories.UserRepository
import lombok.RequiredArgsConstructor
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Service

@Service
@RequiredArgsConstructor
class QAUserDetailsService(
private val userRepository: UserRepository,
) : UserDetailsService {

@Throws(UsernameNotFoundException::class)
override fun loadUserByUsername(username: String): UserDetails {
return userRepository.findByUsername(username)
.map { user -> QAUserDetails(user) }
.orElseThrow { UsernameNotFoundException("No user found") }
}
}
Loading