Skip to content

Commit

Permalink
feat(api): add /api/v2/users and deprecate /api/v1/users
Browse files Browse the repository at this point in the history
simplify the user DTO classes
make api logout endpoint to version agnostic
  • Loading branch information
gotson committed Mar 3, 2022
1 parent f1ab136 commit fa04d95
Show file tree
Hide file tree
Showing 14 changed files with 508 additions and 71 deletions.
6 changes: 3 additions & 3 deletions komga-webui/src/components/UsersList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,15 @@ export default Vue.extend({
modalDeleteUser: false,
userToDelete: {} as UserDto,
modalEditSharedLibraries: false,
userToEditSharedLibraries: {} as UserWithSharedLibrariesDto,
userToEditSharedLibraries: {} as UserDto,
modalEditUser: false,
userToEdit: {} as UserDto,
modalChangePassword: false,
userToChangePassword: {} as UserDto,
usersLastActivity: {} as any,
}),
computed: {
users(): UserWithSharedLibrariesDto[] {
users(): UserDto[] {
return this.$store.state.komgaUsers.users
},
me(): UserDto {
Expand All @@ -175,7 +175,7 @@ export default Vue.extend({
this.userToDelete = user
this.modalDeleteUser = true
},
editSharedLibraries(user: UserWithSharedLibrariesDto) {
editSharedLibraries(user: UserDto) {
this.userToEditSharedLibraries = user
this.modalEditSharedLibraries = true
},
Expand Down
6 changes: 3 additions & 3 deletions komga-webui/src/components/dialogs/UserEditDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ export default Vue.extend({
},
async editUser() {
try {
const roles = {
const patch = {
roles: this.roles,
} as RolesUpdateDto
} as UserUpdateDto
await this.$store.dispatch('updateUserRoles', {userId: this.user.id, roles: roles})
await this.$store.dispatch('updateUser', {userId: this.user.id, patch: patch})
} catch (e) {
this.$eventHub.$emit(ERROR, {message: e.message} as ErrorEvent)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ export default Vue.extend({
},
},
methods: {
dialogReset(user: UserWithSharedLibrariesDto) {
dialogReset(user: UserDto) {
this.allLibraries = user.sharedAllLibraries
if (user.sharedAllLibraries) {
this.selectedLibraries = this.libraries.map(x => x.id)
} else {
this.selectedLibraries = user.sharedLibraries.map(x => x.id)
this.selectedLibraries = user.sharedLibrariesIds
}
},
dialogCancel() {
Expand All @@ -110,12 +110,14 @@ export default Vue.extend({
},
async editUser() {
try {
const sharedLibraries = {
all: this.allLibraries,
libraryIds: this.selectedLibraries,
} as SharedLibrariesUpdateDto
const patch = {
sharedLibraries: {
all: this.allLibraries,
libraryIds: this.selectedLibraries,
},
} as UserUpdateDto
await this.$store.dispatch('updateUserSharedLibraries', {user: this.user, sharedLibraries: sharedLibraries})
await this.$store.dispatch('updateUser', {userId: this.user.id, patch: patch})
} catch (e) {
this.$eventHub.$emit(ERROR, {message: e.message} as ErrorEvent)
}
Expand Down
12 changes: 4 additions & 8 deletions komga-webui/src/plugins/komga-users.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let service: KomgaUsersService
const vuexModule: Module<any, any> = {
state: {
me: {} as UserDto,
users: [] as UserWithSharedLibrariesDto[],
users: [] as UserDto[],
},
getters: {
meAdmin: state => state.me.hasOwnProperty('roles') && state.me.roles.includes(UserRoles.ADMIN),
Expand All @@ -21,7 +21,7 @@ const vuexModule: Module<any, any> = {
setMe (state, user: UserDto) {
state.me = user
},
setAllUsers (state, users: UserWithSharedLibrariesDto[]) {
setAllUsers (state, users: UserDto[]) {
state.users = users
},
},
Expand All @@ -46,18 +46,14 @@ const vuexModule: Module<any, any> = {
await service.postUser(user)
dispatch('getAllUsers')
},
async updateUserRoles ({ dispatch }, { userId, roles }: { userId: string, roles: RolesUpdateDto }) {
await service.patchUserRoles(userId, roles)
async updateUser ({ dispatch }, { userId, patch }: { userId: string, patch: UserUpdateDto }) {
await service.patchUser(userId, patch)
dispatch('getAllUsers')
},
async deleteUser ({ dispatch }, user: UserDto) {
await service.deleteUser(user)
dispatch('getAllUsers')
},
async updateUserSharedLibraries ({ dispatch }, { user, sharedLibraries }: { user: UserDto, sharedLibraries: SharedLibrariesUpdateDto }) {
await service.patchUserSharedLibraries(user, sharedLibraries)
dispatch('getAllUsers')
},
},
}

Expand Down
24 changes: 6 additions & 18 deletions komga-webui/src/services/komga-users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {AxiosInstance} from 'axios'

const qs = require('qs')

const API_USERS = '/api/v1/users'
const API_USERS = '/api/v2/users'

export default class KomgaUsersService {
private http: AxiosInstance
Expand Down Expand Up @@ -50,7 +50,7 @@ export default class KomgaUsersService {
}
}

async getAll(): Promise<UserWithSharedLibrariesDto[]> {
async getAll(): Promise<UserDto[]> {
try {
return (await this.http.get(`${API_USERS}`)).data
} catch (e) {
Expand All @@ -74,9 +74,9 @@ export default class KomgaUsersService {
}
}

async patchUserRoles(userId: string, roles: RolesUpdateDto): Promise<UserDto> {
async patchUser(userId: string, patch: UserUpdateDto) {
try {
return (await this.http.patch(`${API_USERS}/${userId}`, roles)).data
await this.http.patch(`${API_USERS}/${userId}`, patch)
} catch (e) {
let msg = `An error occurred while trying to patch user '${userId}'`
if (e.response.data.message) {
Expand All @@ -100,7 +100,7 @@ export default class KomgaUsersService {

async patchUserPassword(user: UserDto, newPassword: PasswordUpdateDto) {
try {
return (await this.http.patch(`${API_USERS}/${user.id}/password`, newPassword)).data
await this.http.patch(`${API_USERS}/${user.id}/password`, newPassword)
} catch (e) {
let msg = `An error occurred while trying to update password for user ${user.email}`
if (e.response.data.message) {
Expand All @@ -110,21 +110,9 @@ export default class KomgaUsersService {
}
}

async patchUserSharedLibraries(user: UserDto, sharedLibrariesUpdateDto: SharedLibrariesUpdateDto) {
try {
return (await this.http.patch(`${API_USERS}/${user.id}/shared-libraries`, sharedLibrariesUpdateDto)).data
} catch (e) {
let msg = `An error occurred while trying to update shared libraries for user ${user.email}`
if (e.response.data.message) {
msg += `: ${e.response.data.message}`
}
throw new Error(msg)
}
}

async logout() {
try {
await this.http.post(`${API_USERS}/logout`)
await this.http.post('api/logout')
} catch (e) {
let msg = 'An error occurred while trying to logout'
if (e.response.data.message) {
Expand Down
25 changes: 7 additions & 18 deletions komga-webui/src/types/komga-users.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
interface UserDto {
id: string,
email: string,
roles: string[]
}

interface UserWithSharedLibrariesDto {
id: string,
email: string,
roles: string[],
sharedAllLibraries: boolean,
sharedLibraries: SharedLibraryDto[]
}

interface SharedLibraryDto {
id: string
sharedLibrariesIds: string[]
}

interface UserCreationDto {
Expand All @@ -25,13 +15,12 @@ interface PasswordUpdateDto {
password: string
}

interface SharedLibrariesUpdateDto {
all: boolean,
libraryIds: string[]
}

interface RolesUpdateDto {
roles: string[]
interface UserUpdateDto {
roles?: string[],
sharedLibraries?: {
all: boolean,
libraryIds: string[]
},
}

interface AuthenticationActivityDto {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class SecurityConfiguration(
it.authenticationDetailsSource(userAgentWebAuthenticationDetailsSource)
}
.logout {
it.logoutUrl("/api/v1/users/logout")
it.logoutUrl("/api/logout")
it.deleteCookies(sessionCookieName)
it.invalidateHttpSession(true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package org.gotson.komga.interfaces.api.rest

import org.gotson.komga.domain.model.KomgaUser
import org.gotson.komga.domain.service.KomgaUserLifecycle
import org.gotson.komga.interfaces.api.rest.dto.UserDto
import org.gotson.komga.interfaces.api.rest.dto.toDto
import org.gotson.komga.interfaces.api.rest.dto.UserDtoV2
import org.gotson.komga.interfaces.api.rest.dto.toDtoV2
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.validation.annotation.Validated
Expand All @@ -30,7 +30,7 @@ class ClaimController(
fun claimAdmin(
@Email(regexp = ".+@.+\\..+") @RequestHeader("X-Komga-Email") email: String,
@NotBlank @RequestHeader("X-Komga-Password") password: String,
): UserDto {
): UserDtoV2 {
if (userDetailsLifecycle.countUsers() > 0)
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "This server has already been claimed")

Expand All @@ -40,7 +40,7 @@ class ClaimController(
password = password,
roleAdmin = true,
),
).toDto()
).toDtoV2()
}

data class ClaimStatus(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("DEPRECATION")

package org.gotson.komga.interfaces.api.rest

import io.swagger.v3.oas.annotations.Parameter
Expand Down Expand Up @@ -46,9 +48,10 @@ import javax.validation.Valid

private val logger = KotlinLogging.logger {}

@Deprecated("User api/v2/users instead")
@RestController
@RequestMapping("api/v1/users", produces = [MediaType.APPLICATION_JSON_VALUE])
class UserController(
class UserV1Controller(
private val userLifecycle: KomgaUserLifecycle,
private val userRepository: KomgaUserRepository,
private val libraryRepository: LibraryRepository,
Expand All @@ -59,10 +62,12 @@ class UserController(
private val demo = env.activeProfiles.contains("demo")

@GetMapping("me")
@Deprecated("User api/v2/users instead")
fun getMe(@AuthenticationPrincipal principal: KomgaPrincipal): UserDto =
principal.user.toDto()

@PatchMapping("me/password")
@Deprecated("User api/v2/users instead")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun updateMyPassword(
@AuthenticationPrincipal principal: KomgaPrincipal,
Expand All @@ -76,12 +81,14 @@ class UserController(

@GetMapping
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun getAll(): List<UserWithSharedLibrariesDto> =
userRepository.findAll().map { it.toWithSharedLibrariesDto() }

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun addOne(@Valid @RequestBody newUser: UserCreationDto): UserDto =
try {
userLifecycle.createUser(newUser.toDomain()).toDto()
Expand All @@ -92,6 +99,7 @@ class UserController(
@DeleteMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN') and #principal.user.id != #id")
@Deprecated("User api/v2/users instead")
fun delete(
@PathVariable id: String,
@AuthenticationPrincipal principal: KomgaPrincipal,
Expand All @@ -104,6 +112,7 @@ class UserController(
@PatchMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN') and #principal.user.id != #id")
@Deprecated("User api/v2/users instead")
fun updateUserRoles(
@PathVariable id: String,
@Valid @RequestBody patch: RolesUpdateDto,
Expand All @@ -123,6 +132,7 @@ class UserController(
@PatchMapping("{id}/password")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN') or #principal.user.id == #id")
@Deprecated("User api/v2/users instead")
fun updatePassword(
@PathVariable id: String,
@AuthenticationPrincipal principal: KomgaPrincipal,
Expand All @@ -137,6 +147,7 @@ class UserController(
@PatchMapping("{id}/shared-libraries")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun updateSharesLibraries(
@PathVariable id: String,
@Valid @RequestBody sharedLibrariesUpdateDto: SharedLibrariesUpdateDto,
Expand All @@ -156,6 +167,7 @@ class UserController(

@GetMapping("me/authentication-activity")
@PageableAsQueryParam
@Deprecated("User api/v2/users instead")
fun getMyAuthenticationActivity(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
Expand All @@ -180,6 +192,7 @@ class UserController(
@GetMapping("authentication-activity")
@PageableAsQueryParam
@PreAuthorize("hasRole('$ROLE_ADMIN')")
@Deprecated("User api/v2/users instead")
fun getAuthenticationActivity(
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
Expand All @@ -201,6 +214,7 @@ class UserController(

@GetMapping("{id}/authentication-activity/latest")
@PreAuthorize("hasRole('$ROLE_ADMIN') or #principal.user.id == #id")
@Deprecated("User api/v2/users instead")
fun getLatestAuthenticationActivityForUser(
@PathVariable id: String,
@AuthenticationPrincipal principal: KomgaPrincipal,
Expand Down
Loading

0 comments on commit fa04d95

Please sign in to comment.