비밀번호 변경
This commit is contained in:
@@ -32,6 +32,7 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
|
|||||||
showTCPServerEdit: false,
|
showTCPServerEdit: false,
|
||||||
showQueueEdit: false,
|
showQueueEdit: false,
|
||||||
showUserEdit: false,
|
showUserEdit: false,
|
||||||
|
showPasswordChange: false,
|
||||||
selectedItem: null,
|
selectedItem: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,6 +78,9 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
|
|||||||
case 'variables-show':
|
case 'variables-show':
|
||||||
setDialogState(prev => ({ ...prev, showVariableModal: true }));
|
setDialogState(prev => ({ ...prev, showVariableModal: true }));
|
||||||
break;
|
break;
|
||||||
|
case 'change-password':
|
||||||
|
setDialogState(prev => ({ ...prev, showPasswordChange: true }));
|
||||||
|
break;
|
||||||
case 'logout':
|
case 'logout':
|
||||||
const success = await handleLogout();
|
const success = await handleLogout();
|
||||||
if (success) {
|
if (success) {
|
||||||
@@ -139,6 +143,7 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
|
|||||||
showQueueEdit={dialogState.showQueueEdit}
|
showQueueEdit={dialogState.showQueueEdit}
|
||||||
showUserEdit={dialogState.showUserEdit}
|
showUserEdit={dialogState.showUserEdit}
|
||||||
selectedItem={dialogState.selectedItem}
|
selectedItem={dialogState.selectedItem}
|
||||||
|
showPasswordChange={dialogState.showPasswordChange}
|
||||||
onDialogChange={handleDialogChange}
|
onDialogChange={handleDialogChange}
|
||||||
/>
|
/>
|
||||||
<div className="flex-1 mt-14">
|
<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 TCPServerEditDialog from '@/components/tcp-servers/TCPServerEditDialog.jsx';
|
||||||
import QueueEditDialog from '@/components/queues/QueueEditDialog.jsx';
|
import QueueEditDialog from '@/components/queues/QueueEditDialog.jsx';
|
||||||
import UserEditDialog from '@/components/users/UserEditDialog.jsx';
|
import UserEditDialog from '@/components/users/UserEditDialog.jsx';
|
||||||
|
import PasswordChangeDialog from '@/components/account/PasswordChangeDialog.jsx';
|
||||||
|
|
||||||
const DialogManager = ({
|
const DialogManager = ({
|
||||||
showAddApi,
|
showAddApi,
|
||||||
@@ -18,6 +19,7 @@ const DialogManager = ({
|
|||||||
showQueueEdit,
|
showQueueEdit,
|
||||||
showUserEdit,
|
showUserEdit,
|
||||||
selectedItem,
|
selectedItem,
|
||||||
|
showPasswordChange,
|
||||||
onDialogChange
|
onDialogChange
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
@@ -58,6 +60,10 @@ const DialogManager = ({
|
|||||||
onOpenChange={(isOpen) => onDialogChange('showUserEdit', isOpen)}
|
onOpenChange={(isOpen) => onDialogChange('showUserEdit', isOpen)}
|
||||||
item={selectedItem}
|
item={selectedItem}
|
||||||
/>
|
/>
|
||||||
|
<PasswordChangeDialog
|
||||||
|
open={showPasswordChange}
|
||||||
|
onOpenChange={(isOpen) => onDialogChange('showPasswordChange', isOpen)}
|
||||||
|
/>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,70 @@
|
|||||||
package com.eactive.testmaster.user.controller;
|
package com.eactive.testmaster.user.controller;
|
||||||
|
|
||||||
import com.eactive.testmaster.common.util.SecurityUtil;
|
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.entity.User;
|
||||||
|
import com.eactive.testmaster.user.service.UserService;
|
||||||
|
import java.util.HashMap;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.annotation.Secured;
|
import org.springframework.security.access.annotation.Secured;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.ui.ModelMap;
|
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.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;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@RequestMapping("/api/account")
|
||||||
public class AccountApiController {
|
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")
|
@Secured("ROLE_API_TESTER")
|
||||||
public ResponseEntity<User> account(ModelMap model) {
|
public ResponseEntity<User> account(ModelMap model) {
|
||||||
return ResponseEntity.ok().body(SecurityUtil.getCurrentUser());
|
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