비밀번호 변경
This commit is contained in:
@@ -32,6 +32,7 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
|
||||
showTCPServerEdit: false,
|
||||
showQueueEdit: false,
|
||||
showUserEdit: false,
|
||||
showPasswordChange: false,
|
||||
selectedItem: null,
|
||||
});
|
||||
|
||||
@@ -77,6 +78,9 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
|
||||
case 'variables-show':
|
||||
setDialogState(prev => ({ ...prev, showVariableModal: true }));
|
||||
break;
|
||||
case 'change-password':
|
||||
setDialogState(prev => ({ ...prev, showPasswordChange: true }));
|
||||
break;
|
||||
case 'logout':
|
||||
const success = await handleLogout();
|
||||
if (success) {
|
||||
@@ -139,6 +143,7 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
|
||||
showQueueEdit={dialogState.showQueueEdit}
|
||||
showUserEdit={dialogState.showUserEdit}
|
||||
selectedItem={dialogState.selectedItem}
|
||||
showPasswordChange={dialogState.showPasswordChange}
|
||||
onDialogChange={handleDialogChange}
|
||||
/>
|
||||
<div className="flex-1 mt-14">
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { getCSRFToken } from '@/services/token.js';
|
||||
|
||||
const PasswordChangeDialog = ({ open, onOpenChange }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPassword2: ''
|
||||
});
|
||||
const [message, setMessage] = useState({ type: '', content: '' });
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPassword2: ''
|
||||
});
|
||||
setMessage({ type: '', content: '' });
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
resetForm();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
if (!formData.oldPassword) {
|
||||
setMessage({ type: 'error', content: '현재 비밀번호를 입력해주세요.' });
|
||||
return false;
|
||||
}
|
||||
if (!formData.newPassword) {
|
||||
setMessage({ type: 'error', content: '새 비밀번호를 입력해주세요.' });
|
||||
return false;
|
||||
}
|
||||
if (formData.newPassword !== formData.newPassword2) {
|
||||
setMessage({ type: 'error', content: '새 비밀번호가 일치하지 않습니다.' });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setMessage({ type: '', content: '' });
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/account/password', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-XSRF-TOKEN': getCSRFToken()
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || '비밀번호 변경에 실패했습니다.');
|
||||
}
|
||||
|
||||
setMessage({ type: 'success', content: '비밀번호가 성공적으로 변경되었습니다.' });
|
||||
setTimeout(() => {
|
||||
handleClose();
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
setMessage({ type: 'error', content: error.message });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleClose}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>비밀번호 변경</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="grid gap-4 py-4">
|
||||
{message.content && (
|
||||
<Alert variant={message.type === 'error' ? 'destructive' : 'default'}>
|
||||
<AlertDescription>{message.content}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="oldPassword" className="text-right">
|
||||
현재 비밀번호
|
||||
</Label>
|
||||
<Input
|
||||
id="oldPassword"
|
||||
type="password"
|
||||
value={formData.oldPassword}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, oldPassword: e.target.value }))}
|
||||
className="col-span-3"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="newPassword" className="text-right">
|
||||
새 비밀번호
|
||||
</Label>
|
||||
<Input
|
||||
id="newPassword"
|
||||
type="password"
|
||||
value={formData.newPassword}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, newPassword: e.target.value }))}
|
||||
className="col-span-3"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="newPassword2" className="text-right">
|
||||
새 비밀번호 확인
|
||||
</Label>
|
||||
<Input
|
||||
id="newPassword2"
|
||||
type="password"
|
||||
value={formData.newPassword2}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, newPassword2: e.target.value }))}
|
||||
className="col-span-3"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="secondary" onClick={handleClose}>
|
||||
취소
|
||||
</Button>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{isLoading ? '변경 중...' : '변경'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordChangeDialog;
|
||||
@@ -7,6 +7,7 @@ import MockRouteEditDialog from '@/components/mock-route/MockRouteEditDialog.jsx
|
||||
import TCPServerEditDialog from '@/components/tcp-servers/TCPServerEditDialog.jsx';
|
||||
import QueueEditDialog from '@/components/queues/QueueEditDialog.jsx';
|
||||
import UserEditDialog from '@/components/users/UserEditDialog.jsx';
|
||||
import PasswordChangeDialog from '@/components/account/PasswordChangeDialog.jsx';
|
||||
|
||||
const DialogManager = ({
|
||||
showAddApi,
|
||||
@@ -18,6 +19,7 @@ const DialogManager = ({
|
||||
showQueueEdit,
|
||||
showUserEdit,
|
||||
selectedItem,
|
||||
showPasswordChange,
|
||||
onDialogChange
|
||||
}) => {
|
||||
|
||||
@@ -58,6 +60,10 @@ const DialogManager = ({
|
||||
onOpenChange={(isOpen) => onDialogChange('showUserEdit', isOpen)}
|
||||
item={selectedItem}
|
||||
/>
|
||||
<PasswordChangeDialog
|
||||
open={showPasswordChange}
|
||||
onOpenChange={(isOpen) => onDialogChange('showPasswordChange', isOpen)}
|
||||
/>
|
||||
</>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,70 @@
|
||||
package com.eactive.testmaster.user.controller;
|
||||
|
||||
import com.eactive.testmaster.common.util.SecurityUtil;
|
||||
import com.eactive.testmaster.user.dto.PasswordChangeRequestDTO;
|
||||
import com.eactive.testmaster.user.entity.User;
|
||||
import com.eactive.testmaster.user.service.UserService;
|
||||
import java.util.HashMap;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
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
|
||||
@RequestMapping("/api/account")
|
||||
public class AccountApiController {
|
||||
|
||||
public AccountApiController() {
|
||||
private final Validator validator;
|
||||
private final UserService userService;
|
||||
|
||||
public AccountApiController(Validator validator, UserService userService) {
|
||||
this.validator = validator;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/api/account")
|
||||
@GetMapping
|
||||
@Secured("ROLE_API_TESTER")
|
||||
public ResponseEntity<User> account(ModelMap model) {
|
||||
return ResponseEntity.ok().body(SecurityUtil.getCurrentUser());
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/password")
|
||||
@Transactional
|
||||
@Secured("ROLE_API_TESTER")
|
||||
public ResponseEntity<?> updatePassword(@RequestBody PasswordChangeRequestDTO passwordChangeRequestDTO) {
|
||||
BindingResult result = new BeanPropertyBindingResult(passwordChangeRequestDTO, "passwordChangeRequest");
|
||||
validator.validate(passwordChangeRequestDTO, result);
|
||||
|
||||
if (result.hasErrors()) {
|
||||
return ResponseEntity.badRequest().body(result.getAllErrors());
|
||||
}
|
||||
|
||||
try {
|
||||
String resultMsg = userService.updateUserPassword(
|
||||
SecurityUtil.getCurrentUserEsntlId(),
|
||||
passwordChangeRequestDTO.getOldPassword(),
|
||||
passwordChangeRequestDTO.getNewPassword(),
|
||||
passwordChangeRequestDTO.getNewPassword2()
|
||||
);
|
||||
|
||||
return ResponseEntity.ok().body(new HashMap<String, String>() {{
|
||||
put("message", resultMsg);
|
||||
}});
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(new HashMap<String, String>() {{
|
||||
put("error", e.getMessage());
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user