Files
minio-webui/frontend/src/components/Users/CreateUserDialog.tsx
T
paul a27b405392 fix: Clarify MinIO access credentials in UI
- Update CreateUserDialog labels to show 'Username (Access Key)' and 'Password (Secret Key)'
- Add helper text explaining these are MinIO credentials
- Enhanced success display to show credentials with proper MinIO terminology
- Add MinIO CLI connection examples in credential displays
- Fix confusion about where to get access key and secret key

This addresses user feedback about unclear credential terminology and aligns
with MinIO documentation where username = Access Key and password = Secret Key.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-23 17:33:13 +02:00

276 lines
8.4 KiB
TypeScript

import React, { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
TextField,
Box,
Typography,
Alert,
IconButton,
InputAdornment,
List,
ListItem,
ListItemIcon,
ListItemText,
Tooltip,
} from '@mui/material';
import {
Visibility,
VisibilityOff,
CheckCircle,
Cancel,
ContentCopy,
} from '@mui/icons-material';
import userService from '../../services/userService';
interface CreateUserDialogProps {
open: boolean;
onClose: () => void;
onSuccess: () => void;
}
const CreateUserDialog: React.FC<CreateUserDialogProps> = ({
open,
onClose,
onSuccess,
}) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [showSuccess, setShowSuccess] = useState(false);
const passwordValidation = userService.validatePassword(password);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
if (!username.trim()) {
setError('Username is required');
return;
}
if (!passwordValidation.valid) {
setError('Please fix password validation errors');
return;
}
setLoading(true);
try {
await userService.createUser({
accessKey: username,
secretKey: password,
});
setShowSuccess(true);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to create user');
setLoading(false);
}
};
const handleCopyCredentials = () => {
const credentials = `Username: ${username}\nPassword: ${password}`;
navigator.clipboard.writeText(credentials);
};
const handleClose = () => {
if (!showSuccess) {
onClose();
resetForm();
}
};
const handleSuccessClose = () => {
onSuccess();
onClose();
resetForm();
};
const resetForm = () => {
setUsername('');
setPassword('');
setShowPassword(false);
setError('');
setShowSuccess(false);
setLoading(false);
};
const getValidationIcon = (isValid: boolean) => {
return isValid ? (
<CheckCircle color="success" fontSize="small" />
) : (
<Cancel color="error" fontSize="small" />
);
};
return (
<Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
<form onSubmit={handleSubmit}>
<DialogTitle>
{showSuccess ? 'User Created Successfully' : 'Create New User'}
</DialogTitle>
<DialogContent>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{!showSuccess ? (
<>
<TextField
autoFocus
margin="dense"
label="Username (Access Key)"
fullWidth
value={username}
onChange={(e) => setUsername(e.target.value)}
required
disabled={loading}
helperText="This will be the user's Access Key for MinIO"
sx={{ mb: 2 }}
/>
<TextField
margin="dense"
label="Password (Secret Key)"
type={showPassword ? 'text' : 'password'}
fullWidth
value={password}
onChange={(e) => setPassword(e.target.value)}
required
disabled={loading}
helperText="This will be the user's Secret Key for MinIO"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={() => setShowPassword(!showPassword)}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
{password && (
<Box mt={2}>
<Typography variant="subtitle2" gutterBottom>
Password Requirements:
</Typography>
<List dense>
<ListItem>
<ListItemIcon>
{getValidationIcon(password.length >= 8)}
</ListItemIcon>
<ListItemText
primary="At least 8 characters"
primaryTypographyProps={{
color: password.length >= 8 ? 'text.primary' : 'text.secondary',
}}
/>
</ListItem>
<ListItem>
<ListItemIcon>
{getValidationIcon(/[A-Z]/.test(password))}
</ListItemIcon>
<ListItemText
primary="At least one uppercase letter"
primaryTypographyProps={{
color: /[A-Z]/.test(password) ? 'text.primary' : 'text.secondary',
}}
/>
</ListItem>
<ListItem>
<ListItemIcon>
{getValidationIcon(/[a-z]/.test(password))}
</ListItemIcon>
<ListItemText
primary="At least one lowercase letter"
primaryTypographyProps={{
color: /[a-z]/.test(password) ? 'text.primary' : 'text.secondary',
}}
/>
</ListItem>
<ListItem>
<ListItemIcon>
{getValidationIcon(/[0-9]/.test(password))}
</ListItemIcon>
<ListItemText
primary="At least one number"
primaryTypographyProps={{
color: /[0-9]/.test(password) ? 'text.primary' : 'text.secondary',
}}
/>
</ListItem>
</List>
</Box>
)}
</>
) : (
<Box>
<Alert severity="success" sx={{ mb: 2 }}>
User created successfully! Save these MinIO credentials securely - the secret key cannot be retrieved later.
</Alert>
<Typography variant="h6" gutterBottom>
MinIO Access Credentials
</Typography>
<Box sx={{ bgcolor: 'grey.100', p: 2, borderRadius: 1, mb: 2 }}>
<Typography variant="body2" sx={{ fontFamily: 'monospace' }}>
<strong>Access Key:</strong> {username}
<br />
<strong>Secret Key:</strong> {password}
</Typography>
</Box>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
Use these credentials to connect via MinIO Client:
</Typography>
<Box sx={{ bgcolor: 'grey.900', color: 'grey.100', p: 1, borderRadius: 1, fontFamily: 'monospace', fontSize: '0.875rem', mb: 2 }}>
mc alias set myminio http://your-minio-server:9000 {username} {password}
</Box>
<Tooltip title="Copy credentials to clipboard">
<Button
variant="outlined"
startIcon={<ContentCopy />}
onClick={handleCopyCredentials}
fullWidth
>
Copy Credentials
</Button>
</Tooltip>
</Box>
)}
</DialogContent>
<DialogActions>
{!showSuccess ? (
<>
<Button onClick={handleClose} disabled={loading}>
Cancel
</Button>
<Button
type="submit"
variant="contained"
disabled={loading || !passwordValidation.valid}
>
Create User
</Button>
</>
) : (
<Button onClick={handleSuccessClose} variant="contained">
Close
</Button>
)}
</DialogActions>
</form>
</Dialog>
);
};
export default CreateUserDialog;