diff --git a/build.sh b/build.sh index 3fee9a1..5633fcc 100644 --- a/build.sh +++ b/build.sh @@ -5,6 +5,7 @@ if [ $? -ne 0 ]; then fi cp -r static build/ cp -r GeoLite2-Country.mmdb build/ +cp -r Logo.png build/static/ mkdir build/images cp client/src/assets/images/icons/cosmos_gray.png build/cosmos_gray.png cp client/src/assets/images/icons/cosmos_gray.png cosmos_gray.png diff --git a/changelog.md b/changelog.md index e9d4d02..b4a8a3b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,15 @@ +## Version 0.3.0 + - Set Max nb simulatneous connections per user + - Set Global Max nb simulatneous connections + - Display nickname on invite page + - Reset self-signed certificates when hostnames changes + - Block based on geo-locations + - Block common bots + - DNS challenge for letsencrypt + - Implement 2 FA + - Implement SMTP to Send Email (password reset / invites) + - Show loading on user rows on actions + ## Version 0.2.0 - URL UI completely redone from scratch - Add new "Smart Shield" feature for easier protection without manual adjustments required diff --git a/client/src/api/users.jsx b/client/src/api/users.jsx index 1975ef2..46a2998 100644 --- a/client/src/api/users.jsx +++ b/client/src/api/users.jsx @@ -100,6 +100,16 @@ function reset2FA(values) { })) } +function resetPassword(values) { + return wrap(fetch('/cosmos/api/password-reset', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(values), + })) +} + export { list, create, @@ -110,5 +120,6 @@ export { deleteUser, new2FA, check2FA, - reset2FA + reset2FA, + resetPassword, }; \ No newline at end of file diff --git a/client/src/components/countrySelect.jsx b/client/src/components/countrySelect.jsx new file mode 100644 index 0000000..0450fa4 --- /dev/null +++ b/client/src/components/countrySelect.jsx @@ -0,0 +1,493 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import TextField from '@mui/material/TextField'; +import Autocomplete from '@mui/material/Autocomplete'; +import { Grid } from '@mui/material'; + +// From https://bitbucket.org/atlassian/atlaskit-mk-2/raw/4ad0e56649c3e6c973e226b7efaeb28cb240ccb0/packages/core/select/src/data/countries.js +const _countries = [ + { code: 'AD', label: 'Andorra', phone: '376' }, + { + code: 'AE', + label: 'United Arab Emirates', + phone: '971', + }, + { code: 'AF', label: 'Afghanistan', phone: '93' }, + { + code: 'AG', + label: 'Antigua and Barbuda', + phone: '1-268', + }, + { code: 'AI', label: 'Anguilla', phone: '1-264' }, + { code: 'AL', label: 'Albania', phone: '355' }, + { code: 'AM', label: 'Armenia', phone: '374' }, + { code: 'AO', label: 'Angola', phone: '244' }, + { code: 'AQ', label: 'Antarctica', phone: '672' }, + { code: 'AR', label: 'Argentina', phone: '54' }, + { code: 'AS', label: 'American Samoa', phone: '1-684' }, + { code: 'AT', label: 'Austria', phone: '43' }, + { + code: 'AU', + label: 'Australia', + phone: '61', + suggested: true, + }, + { code: 'AW', label: 'Aruba', phone: '297' }, + { code: 'AX', label: 'Alland Islands', phone: '358' }, + { code: 'AZ', label: 'Azerbaijan', phone: '994' }, + { + code: 'BA', + label: 'Bosnia and Herzegovina', + phone: '387', + }, + { code: 'BB', label: 'Barbados', phone: '1-246' }, + { code: 'BD', label: 'Bangladesh', phone: '880' }, + { code: 'BE', label: 'Belgium', phone: '32' }, + { code: 'BF', label: 'Burkina Faso', phone: '226' }, + { code: 'BG', label: 'Bulgaria', phone: '359' }, + { code: 'BH', label: 'Bahrain', phone: '973' }, + { code: 'BI', label: 'Burundi', phone: '257' }, + { code: 'BJ', label: 'Benin', phone: '229' }, + { code: 'BL', label: 'Saint Barthelemy', phone: '590' }, + { code: 'BM', label: 'Bermuda', phone: '1-441' }, + { code: 'BN', label: 'Brunei Darussalam', phone: '673' }, + { code: 'BO', label: 'Bolivia', phone: '591' }, + { code: 'BR', label: 'Brazil', phone: '55' }, + { code: 'BS', label: 'Bahamas', phone: '1-242' }, + { code: 'BT', label: 'Bhutan', phone: '975' }, + { code: 'BV', label: 'Bouvet Island', phone: '47' }, + { code: 'BW', label: 'Botswana', phone: '267' }, + { code: 'BY', label: 'Belarus', phone: '375' }, + { code: 'BZ', label: 'Belize', phone: '501' }, + { + code: 'CA', + label: 'Canada', + phone: '1', + suggested: true, + }, + { + code: 'CC', + label: 'Cocos (Keeling) Islands', + phone: '61', + }, + { + code: 'CD', + label: 'Congo, Democratic Republic of the', + phone: '243', + }, + { + code: 'CF', + label: 'Central African Republic', + phone: '236', + }, + { + code: 'CG', + label: 'Congo, Republic of the', + phone: '242', + }, + { code: 'CH', label: 'Switzerland', phone: '41' }, + { code: 'CI', label: "Cote d'Ivoire", phone: '225' }, + { code: 'CK', label: 'Cook Islands', phone: '682' }, + { code: 'CL', label: 'Chile', phone: '56' }, + { code: 'CM', label: 'Cameroon', phone: '237' }, + { code: 'CN', label: 'China', phone: '86' }, + { code: 'CO', label: 'Colombia', phone: '57' }, + { code: 'CR', label: 'Costa Rica', phone: '506' }, + { code: 'CU', label: 'Cuba', phone: '53' }, + { code: 'CV', label: 'Cape Verde', phone: '238' }, + { code: 'CW', label: 'Curacao', phone: '599' }, + { code: 'CX', label: 'Christmas Island', phone: '61' }, + { code: 'CY', label: 'Cyprus', phone: '357' }, + { code: 'CZ', label: 'Czech Republic', phone: '420' }, + { + code: 'DE', + label: 'Germany', + phone: '49', + suggested: true, + }, + { code: 'DJ', label: 'Djibouti', phone: '253' }, + { code: 'DK', label: 'Denmark', phone: '45' }, + { code: 'DM', label: 'Dominica', phone: '1-767' }, + { + code: 'DO', + label: 'Dominican Republic', + phone: '1-809', + }, + { code: 'DZ', label: 'Algeria', phone: '213' }, + { code: 'EC', label: 'Ecuador', phone: '593' }, + { code: 'EE', label: 'Estonia', phone: '372' }, + { code: 'EG', label: 'Egypt', phone: '20' }, + { code: 'EH', label: 'Western Sahara', phone: '212' }, + { code: 'ER', label: 'Eritrea', phone: '291' }, + { code: 'ES', label: 'Spain', phone: '34' }, + { code: 'ET', label: 'Ethiopia', phone: '251' }, + { code: 'FI', label: 'Finland', phone: '358' }, + { code: 'FJ', label: 'Fiji', phone: '679' }, + { + code: 'FK', + label: 'Falkland Islands (Malvinas)', + phone: '500', + }, + { + code: 'FM', + label: 'Micronesia, Federated States of', + phone: '691', + }, + { code: 'FO', label: 'Faroe Islands', phone: '298' }, + { + code: 'FR', + label: 'France', + phone: '33', + suggested: true, + }, + { code: 'GA', label: 'Gabon', phone: '241' }, + { code: 'GB', label: 'United Kingdom', phone: '44' }, + { code: 'GD', label: 'Grenada', phone: '1-473' }, + { code: 'GE', label: 'Georgia', phone: '995' }, + { code: 'GF', label: 'French Guiana', phone: '594' }, + { code: 'GG', label: 'Guernsey', phone: '44' }, + { code: 'GH', label: 'Ghana', phone: '233' }, + { code: 'GI', label: 'Gibraltar', phone: '350' }, + { code: 'GL', label: 'Greenland', phone: '299' }, + { code: 'GM', label: 'Gambia', phone: '220' }, + { code: 'GN', label: 'Guinea', phone: '224' }, + { code: 'GP', label: 'Guadeloupe', phone: '590' }, + { code: 'GQ', label: 'Equatorial Guinea', phone: '240' }, + { code: 'GR', label: 'Greece', phone: '30' }, + { + code: 'GS', + label: 'South Georgia and the South Sandwich Islands', + phone: '500', + }, + { code: 'GT', label: 'Guatemala', phone: '502' }, + { code: 'GU', label: 'Guam', phone: '1-671' }, + { code: 'GW', label: 'Guinea-Bissau', phone: '245' }, + { code: 'GY', label: 'Guyana', phone: '592' }, + { code: 'HK', label: 'Hong Kong', phone: '852' }, + { + code: 'HM', + label: 'Heard Island and McDonald Islands', + phone: '672', + }, + { code: 'HN', label: 'Honduras', phone: '504' }, + { code: 'HR', label: 'Croatia', phone: '385' }, + { code: 'HT', label: 'Haiti', phone: '509' }, + { code: 'HU', label: 'Hungary', phone: '36' }, + { code: 'ID', label: 'Indonesia', phone: '62' }, + { code: 'IE', label: 'Ireland', phone: '353' }, + { code: 'IL', label: 'Israel', phone: '972' }, + { code: 'IM', label: 'Isle of Man', phone: '44' }, + { code: 'IN', label: 'India', phone: '91' }, + { + code: 'IO', + label: 'British Indian Ocean Territory', + phone: '246', + }, + { code: 'IQ', label: 'Iraq', phone: '964' }, + { + code: 'IR', + label: 'Iran, Islamic Republic of', + phone: '98', + }, + { code: 'IS', label: 'Iceland', phone: '354' }, + { code: 'IT', label: 'Italy', phone: '39' }, + { code: 'JE', label: 'Jersey', phone: '44' }, + { code: 'JM', label: 'Jamaica', phone: '1-876' }, + { code: 'JO', label: 'Jordan', phone: '962' }, + { + code: 'JP', + label: 'Japan', + phone: '81', + suggested: true, + }, + { code: 'KE', label: 'Kenya', phone: '254' }, + { code: 'KG', label: 'Kyrgyzstan', phone: '996' }, + { code: 'KH', label: 'Cambodia', phone: '855' }, + { code: 'KI', label: 'Kiribati', phone: '686' }, + { code: 'KM', label: 'Comoros', phone: '269' }, + { + code: 'KN', + label: 'Saint Kitts and Nevis', + phone: '1-869', + }, + { + code: 'KP', + label: "Korea, Democratic People's Republic of", + phone: '850', + }, + { code: 'KR', label: 'Korea, Republic of', phone: '82' }, + { code: 'KW', label: 'Kuwait', phone: '965' }, + { code: 'KY', label: 'Cayman Islands', phone: '1-345' }, + { code: 'KZ', label: 'Kazakhstan', phone: '7' }, + { + code: 'LA', + label: "Lao People's Democratic Republic", + phone: '856', + }, + { code: 'LB', label: 'Lebanon', phone: '961' }, + { code: 'LC', label: 'Saint Lucia', phone: '1-758' }, + { code: 'LI', label: 'Liechtenstein', phone: '423' }, + { code: 'LK', label: 'Sri Lanka', phone: '94' }, + { code: 'LR', label: 'Liberia', phone: '231' }, + { code: 'LS', label: 'Lesotho', phone: '266' }, + { code: 'LT', label: 'Lithuania', phone: '370' }, + { code: 'LU', label: 'Luxembourg', phone: '352' }, + { code: 'LV', label: 'Latvia', phone: '371' }, + { code: 'LY', label: 'Libya', phone: '218' }, + { code: 'MA', label: 'Morocco', phone: '212' }, + { code: 'MC', label: 'Monaco', phone: '377' }, + { + code: 'MD', + label: 'Moldova, Republic of', + phone: '373', + }, + { code: 'ME', label: 'Montenegro', phone: '382' }, + { + code: 'MF', + label: 'Saint Martin (French part)', + phone: '590', + }, + { code: 'MG', label: 'Madagascar', phone: '261' }, + { code: 'MH', label: 'Marshall Islands', phone: '692' }, + { + code: 'MK', + label: 'Macedonia, the Former Yugoslav Republic of', + phone: '389', + }, + { code: 'ML', label: 'Mali', phone: '223' }, + { code: 'MM', label: 'Myanmar', phone: '95' }, + { code: 'MN', label: 'Mongolia', phone: '976' }, + { code: 'MO', label: 'Macao', phone: '853' }, + { + code: 'MP', + label: 'Northern Mariana Islands', + phone: '1-670', + }, + { code: 'MQ', label: 'Martinique', phone: '596' }, + { code: 'MR', label: 'Mauritania', phone: '222' }, + { code: 'MS', label: 'Montserrat', phone: '1-664' }, + { code: 'MT', label: 'Malta', phone: '356' }, + { code: 'MU', label: 'Mauritius', phone: '230' }, + { code: 'MV', label: 'Maldives', phone: '960' }, + { code: 'MW', label: 'Malawi', phone: '265' }, + { code: 'MX', label: 'Mexico', phone: '52' }, + { code: 'MY', label: 'Malaysia', phone: '60' }, + { code: 'MZ', label: 'Mozambique', phone: '258' }, + { code: 'NA', label: 'Namibia', phone: '264' }, + { code: 'NC', label: 'New Caledonia', phone: '687' }, + { code: 'NE', label: 'Niger', phone: '227' }, + { code: 'NF', label: 'Norfolk Island', phone: '672' }, + { code: 'NG', label: 'Nigeria', phone: '234' }, + { code: 'NI', label: 'Nicaragua', phone: '505' }, + { code: 'NL', label: 'Netherlands', phone: '31' }, + { code: 'NO', label: 'Norway', phone: '47' }, + { code: 'NP', label: 'Nepal', phone: '977' }, + { code: 'NR', label: 'Nauru', phone: '674' }, + { code: 'NU', label: 'Niue', phone: '683' }, + { code: 'NZ', label: 'New Zealand', phone: '64' }, + { code: 'OM', label: 'Oman', phone: '968' }, + { code: 'PA', label: 'Panama', phone: '507' }, + { code: 'PE', label: 'Peru', phone: '51' }, + { code: 'PF', label: 'French Polynesia', phone: '689' }, + { code: 'PG', label: 'Papua New Guinea', phone: '675' }, + { code: 'PH', label: 'Philippines', phone: '63' }, + { code: 'PK', label: 'Pakistan', phone: '92' }, + { code: 'PL', label: 'Poland', phone: '48' }, + { + code: 'PM', + label: 'Saint Pierre and Miquelon', + phone: '508', + }, + { code: 'PN', label: 'Pitcairn', phone: '870' }, + { code: 'PR', label: 'Puerto Rico', phone: '1' }, + { + code: 'PS', + label: 'Palestine, State of', + phone: '970', + }, + { code: 'PT', label: 'Portugal', phone: '351' }, + { code: 'PW', label: 'Palau', phone: '680' }, + { code: 'PY', label: 'Paraguay', phone: '595' }, + { code: 'QA', label: 'Qatar', phone: '974' }, + { code: 'RE', label: 'Reunion', phone: '262' }, + { code: 'RO', label: 'Romania', phone: '40' }, + { code: 'RS', label: 'Serbia', phone: '381' }, + { code: 'RU', label: 'Russian Federation', phone: '7' }, + { code: 'RW', label: 'Rwanda', phone: '250' }, + { code: 'SA', label: 'Saudi Arabia', phone: '966' }, + { code: 'SB', label: 'Solomon Islands', phone: '677' }, + { code: 'SC', label: 'Seychelles', phone: '248' }, + { code: 'SD', label: 'Sudan', phone: '249' }, + { code: 'SE', label: 'Sweden', phone: '46' }, + { code: 'SG', label: 'Singapore', phone: '65' }, + { code: 'SH', label: 'Saint Helena', phone: '290' }, + { code: 'SI', label: 'Slovenia', phone: '386' }, + { + code: 'SJ', + label: 'Svalbard and Jan Mayen', + phone: '47', + }, + { code: 'SK', label: 'Slovakia', phone: '421' }, + { code: 'SL', label: 'Sierra Leone', phone: '232' }, + { code: 'SM', label: 'San Marino', phone: '378' }, + { code: 'SN', label: 'Senegal', phone: '221' }, + { code: 'SO', label: 'Somalia', phone: '252' }, + { code: 'SR', label: 'Suriname', phone: '597' }, + { code: 'SS', label: 'South Sudan', phone: '211' }, + { + code: 'ST', + label: 'Sao Tome and Principe', + phone: '239', + }, + { code: 'SV', label: 'El Salvador', phone: '503' }, + { + code: 'SX', + label: 'Sint Maarten (Dutch part)', + phone: '1-721', + }, + { + code: 'SY', + label: 'Syrian Arab Republic', + phone: '963', + }, + { code: 'SZ', label: 'Swaziland', phone: '268' }, + { + code: 'TC', + label: 'Turks and Caicos Islands', + phone: '1-649', + }, + { code: 'TD', label: 'Chad', phone: '235' }, + { + code: 'TF', + label: 'French Southern Territories', + phone: '262', + }, + { code: 'TG', label: 'Togo', phone: '228' }, + { code: 'TH', label: 'Thailand', phone: '66' }, + { code: 'TJ', label: 'Tajikistan', phone: '992' }, + { code: 'TK', label: 'Tokelau', phone: '690' }, + { code: 'TL', label: 'Timor-Leste', phone: '670' }, + { code: 'TM', label: 'Turkmenistan', phone: '993' }, + { code: 'TN', label: 'Tunisia', phone: '216' }, + { code: 'TO', label: 'Tonga', phone: '676' }, + { code: 'TR', label: 'Turkey', phone: '90' }, + { + code: 'TT', + label: 'Trinidad and Tobago', + phone: '1-868', + }, + { code: 'TV', label: 'Tuvalu', phone: '688' }, + { + code: 'TW', + label: 'Taiwan, Republic of China', + phone: '886', + }, + { + code: 'TZ', + label: 'United Republic of Tanzania', + phone: '255', + }, + { code: 'UA', label: 'Ukraine', phone: '380' }, + { code: 'UG', label: 'Uganda', phone: '256' }, + { + code: 'US', + label: 'United States', + phone: '1', + suggested: true, + }, + { code: 'UY', label: 'Uruguay', phone: '598' }, + { code: 'UZ', label: 'Uzbekistan', phone: '998' }, + { + code: 'VA', + label: 'Holy See (Vatican City State)', + phone: '379', + }, + { + code: 'VC', + label: 'Saint Vincent and the Grenadines', + phone: '1-784', + }, + { code: 'VE', label: 'Venezuela', phone: '58' }, + { + code: 'VG', + label: 'British Virgin Islands', + phone: '1-284', + }, + { + code: 'VI', + label: 'US Virgin Islands', + phone: '1-340', + }, + { code: 'VN', label: 'Vietnam', phone: '84' }, + { code: 'VU', label: 'Vanuatu', phone: '678' }, + { code: 'WF', label: 'Wallis and Futuna', phone: '681' }, + { code: 'WS', label: 'Samoa', phone: '685' }, + { code: 'XK', label: 'Kosovo', phone: '383' }, + { code: 'YE', label: 'Yemen', phone: '967' }, + { code: 'YT', label: 'Mayotte', phone: '262' }, + { code: 'ZA', label: 'South Africa', phone: '27' }, + { code: 'ZM', label: 'Zambia', phone: '260' }, + { code: 'ZW', label: 'Zimbabwe', phone: '263' }, +]; + +const countries = {}; +const countriesOptions = []; +_countries.forEach((country) => { + countries[country.code] = country; + countriesOptions.push(country.code); +}); + +export default function CountrySelect({name, label, formik}) { + return ( + + { + formik.setFieldValue(name, value) + }} + error={Boolean(formik.touched[name] && formik.errors[name])} + getOptionLabel={(option) =>
 {countries[option].label}
} + renderOption={(props, option) => ( + img': { mr: 2, flexShrink: 0 } }} {...props}> + + {countries[option].label} ({option.code}) +{countries[option].phone} + + )} + renderInput={(params) => ( + + )} + /> +
+ ); +} + +export { + countries +}; \ No newline at end of file diff --git a/client/src/isLoggedIn.jsx b/client/src/isLoggedIn.jsx index aca47fb..830517e 100644 --- a/client/src/isLoggedIn.jsx +++ b/client/src/isLoggedIn.jsx @@ -16,7 +16,7 @@ const IsLoggedIn = () => useEffect(() => { window.location.href = '/ui/newmfa'; } } - }); + }) }, []); export default IsLoggedIn; \ No newline at end of file diff --git a/client/src/pages/authentication/auth-forms/AuthLogin.jsx b/client/src/pages/authentication/auth-forms/AuthLogin.jsx index 1823436..a27ee1c 100644 --- a/client/src/pages/authentication/auth-forms/AuthLogin.jsx +++ b/client/src/pages/authentication/auth-forms/AuthLogin.jsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link, Link as RouterLink } from 'react-router-dom'; // material-ui import { @@ -30,6 +30,7 @@ import AnimateButton from '../../../components/@extended/AnimateButton'; // assets import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; +import { LoadingButton } from '@mui/lab'; // ============================|| FIREBASE - LOGIN ||============================ // @@ -182,10 +183,10 @@ const AuthLogin = () => { /> } label={Keep me sign in} - /> - + />*/} + Forgot Password? - */} + {errors.submit && ( @@ -194,10 +195,9 @@ const AuthLogin = () => { )} - - - + {/* diff --git a/client/src/pages/authentication/auth-forms/AuthRegister.jsx b/client/src/pages/authentication/auth-forms/AuthRegister.jsx index 71a2dfc..a7dbf21 100644 --- a/client/src/pages/authentication/auth-forms/AuthRegister.jsx +++ b/client/src/pages/authentication/auth-forms/AuthRegister.jsx @@ -32,6 +32,7 @@ import { strengthColor, strengthIndicator } from '../../../utils/password-streng // assets import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; +import { LoadingButton } from '@mui/lab'; // ============================|| FIREBASE - REGISTER ||============================ // @@ -178,10 +179,9 @@ const AuthRegister = ({nickname, isRegister, isInviteLink, regkey}) => { )} - - - + diff --git a/client/src/pages/authentication/forgotPassword.jsx b/client/src/pages/authentication/forgotPassword.jsx new file mode 100644 index 0000000..984850b --- /dev/null +++ b/client/src/pages/authentication/forgotPassword.jsx @@ -0,0 +1,122 @@ +import { Link } from 'react-router-dom'; + +// material-ui +import { Button, FormHelperText, Grid, InputLabel, OutlinedInput, Stack, Typography } from '@mui/material'; + +// project import +import AuthWrapper from './AuthWrapper'; +import { Formik } from 'formik'; + +// third-party +import * as Yup from 'yup'; +import * as API from '../../api'; +import { CosmosInputText } from '../config/users/formShortcuts'; +import { useState } from 'react'; + +// ================================|| LOGIN ||================================ // + +const ForgotPassword = () => { + const [isSuccess, setIsSuccess] = useState(false); + + return ( + + + + Password Reset + {/* + Don't have an account? + */} + + + + {!isSuccess && { + try { + API.users.resetPassword(values).then((data) => { + if (data.status == 'error') { + setStatus({ success: false }); + setErrors({ submit: 'Unexpected error. Check your infos or try again later.' }); + setSubmitting(false); + return; + } else { + setStatus({ success: true }); + setSubmitting(false); + setIsSuccess(true); + } + }) + } catch (err) { + setStatus({ success: false }); + setErrors({ submit: err.message }); + setSubmitting(false); + } + }} + > + {(formik) => ( +
+ + + + + + + {formik.errors.submit && ( + + {formik.errors.submit} + + )} + + + + +
+ )} +
} + {isSuccess &&
+ Check your email for a link to reset your password. If it doesn’t appear within a few minutes, check your spam folder. +

+ +
} +
+
+
+)}; + +export default ForgotPassword; diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index c6bad43..0410b4a 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -28,7 +28,8 @@ import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; import AnimateButton from '../../../components/@extended/AnimateButton'; import RestartModal from './restart'; import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined , SyncOutlined, UserOutlined, KeyOutlined } from '@ant-design/icons'; -import { CosmosCheckbox, CosmosInputText, CosmosSelect } from './formShortcuts'; +import { CosmosCheckbox, CosmosFormDivider, CosmosInputPassword, CosmosInputText, CosmosSelect } from './formShortcuts'; +import CountrySelect, { countries } from '../../../components/countrySelect'; const ConfigManagement = () => { @@ -57,6 +58,7 @@ const ConfigManagement = () => { MongoDB: config.MongoDB, LoggingLevel: config.LoggingLevel, RequireMFA: config.RequireMFA, + GeoBlocking: config.BlockedCountries, Hostname: config.HTTPConfig.Hostname, GenerateMissingTLSCert: config.HTTPConfig.GenerateMissingTLSCert, @@ -66,6 +68,14 @@ const ConfigManagement = () => { SSLEmail: config.HTTPConfig.SSLEmail, HTTPSCertificateMode: config.HTTPConfig.HTTPSCertificateMode, DNSChallengeProvider: config.HTTPConfig.DNSChallengeProvider, + + Email_Enabled: config.EmailConfig.Enabled, + Email_Host: config.EmailConfig.Host, + Email_Port: config.EmailConfig.Port, + Email_Username: config.EmailConfig.Username, + Email_Password: config.EmailConfig.Password, + Email_From: config.EmailConfig.From, + Email_UseTLS : config.EmailConfig.UseTLS, }} validationSchema={Yup.object().shape({ Hostname: Yup.string().max(255).required('Hostname is required'), @@ -79,6 +89,7 @@ const ConfigManagement = () => { MongoDB: values.MongoDB, LoggingLevel: values.LoggingLevel, RequireMFA: values.RequireMFA, + BlockedCountries: values.GeoBlocking, HTTPConfig: { ...config.HTTPConfig, Hostname: values.Hostname, @@ -88,6 +99,16 @@ const ConfigManagement = () => { SSLEmail: values.SSLEmail, HTTPSCertificateMode: values.HTTPSCertificateMode, DNSChallengeProvider: values.DNSChallengeProvider, + }, + EmailConfig: { + ...config.EmailConfig, + Enabled: values.Email_Enabled, + Host: values.Email_Host, + Port: values.Email_Port, + Username: values.Email_Username, + Password: values.Email_Password, + From: values.Email_From, + UseTLS: values.Email_UseTLS, } } @@ -116,245 +137,316 @@ const ConfigManagement = () => { > {(formik) => (
- - - - This page allow you to edit the configuration file. Any Environment Variable overwritting configuration won't appear here. - - - - - - - MongoDB connection string. It is advised to use Environment variable to store this securely instead. (Optional) - - {formik.touched.MongoDB && formik.errors.MongoDB && ( - - {formik.errors.MongoDB} - - )} - - - - - - Level of logging (Default: INFO) - - - DEBUG - - - INFO - - - WARNING - - - ERROR - - - - - - - -

- - - - - - Hostname: This will be used to restrict access to your Cosmos Server (Default: 0.0.0.0) - - {formik.touched.Hostname && formik.errors.Hostname && ( - - {formik.errors.Hostname} - - )} - - - - - - HTTP Port (Default: 80) - - {formik.touched.HTTPPort && formik.errors.HTTPPort && ( - - {formik.errors.HTTPPort} - - )} - - - - - - HTTPS Port (Default: 443) - - {formik.touched.HTTPSPort && formik.errors.HTTPSPort && ( - - {formik.errors.HTTPSPort} - - )} - - - - -

- + + - - For security reasons, It is not possible to remotely change the Private keys of any certificates on your instance. It is advised to manually edit the config file, or better, use Environment Variables to store them. + + This page allow you to edit the configuration file. Any Environment Variable overwritting configuration won't appear here. + + + + + + + MongoDB connection string. It is advised to use Environment variable to store this securely instead. (Optional) + + {formik.touched.MongoDB && formik.errors.MongoDB && ( + + {formik.errors.MongoDB} + + )} + + + + + + Level of logging (Default: INFO) + + + DEBUG + + + INFO + + + WARNING + + + ERROR + + + + + - + + + + + Hostname: This will be used to restrict access to your Cosmos Server (Default: 0.0.0.0) + + {formik.touched.Hostname && formik.errors.Hostname && ( + + {formik.errors.Hostname} + + )} + + -{ - formik.values.HTTPSCertificateMode === "LETSENCRYPT" && ( + + + HTTP Port (Default: 80) + + {formik.touched.HTTPPort && formik.errors.HTTPPort && ( + + {formik.errors.HTTPPort} + + )} + + + + + + HTTPS Port (Default: 443) + + {formik.touched.HTTPSPort && formik.errors.HTTPSPort && ( + + {formik.errors.HTTPSPort} + + )} + + + + + + + + This allow you to setup an SMTP server for Cosmos to send emails such as password reset emails and invites. + + + + {formik.values.Email_Enabled && (<> - ) - } - - { - formik.values.HTTPSCertificateMode === "LETSENCRYPT" && ( + - ) - } - - - } - label="Generate missing Authentication Certificates automatically (Default: true)" + - - + + + + + + )} + + + + + + + + + Geo-Blocking: (Those countries will be blocked from accessing your server) + + + + + + + + + + + For security reasons, It is not possible to remotely change the Private keys of any certificates on your instance. It is advised to manually edit the config file, or better, use Environment Variables to store them. + + + + + { + formik.values.HTTPSCertificateMode === "LETSENCRYPT" && ( + + ) + } + + { + formik.values.HTTPSCertificateMode === "LETSENCRYPT" && ( + + ) + } + + + + } + label="Generate missing Authentication Certificates automatically (Default: true)" + /> + + + + +

Authentication Public Key

+ +
+                        {config.HTTPConfig.AuthPublicKey}
+                      
+
+
+ + +

Root HTTPS Public Key

+ +
+                        {config.HTTPConfig.TLSCert}
+                      
+
+
+ +
+
+ + + {formik.errors.submit && ( + + {formik.errors.submit} + + )} -

Authentication Public Key

- -
-                      {config.HTTPConfig.AuthPublicKey}
-                    
-
+ + +
- - -

Root HTTPS Public Key

- -
-                      {config.HTTPConfig.TLSCert}
-                    
-
-
- - -
- -

- - - {formik.errors.submit && ( - - {formik.errors.submit} - - )} - - - - - - +
+
)} diff --git a/client/src/pages/config/users/formShortcuts.jsx b/client/src/pages/config/users/formShortcuts.jsx index 6a1ed9a..11c8370 100644 --- a/client/src/pages/config/users/formShortcuts.jsx +++ b/client/src/pages/config/users/formShortcuts.jsx @@ -52,7 +52,7 @@ export const CosmosInputText = ({ name, style, multiline, type, placeholder, onC } -export const CosmosInputPassword = ({ name, type, placeholder, onChange, label, formik }) => { +export const CosmosInputPassword = ({ name, noStrength, type, placeholder, onChange, label, formik }) => { const [level, setLevel] = React.useState(); const [showPassword, setShowPassword] = React.useState(false); const handleClickShowPassword = () => { @@ -108,7 +108,7 @@ export const CosmosInputPassword = ({ name, type, placeholder, onChange, label, )} - + {!noStrength && @@ -119,7 +119,7 @@ export const CosmosInputPassword = ({ name, type, placeholder, onChange, label, - + } } diff --git a/client/src/pages/config/users/usermanagement.jsx b/client/src/pages/config/users/usermanagement.jsx index 93524a0..f1129c4 100644 --- a/client/src/pages/config/users/usermanagement.jsx +++ b/client/src/pages/config/users/usermanagement.jsx @@ -30,6 +30,7 @@ const UserManagement = () => { const [openDeleteForm, setOpenDeleteForm] = React.useState(false); const [openInviteForm, setOpenInviteForm] = React.useState(false); const [toAction, setToAction] = React.useState(null); + const [loadingRow, setLoadingRow] = React.useState(null); const roles = ['Guest', 'User', 'Admin'] @@ -39,6 +40,7 @@ const UserManagement = () => { setIsLoading(true); API.users.list() .then(data => { + setLoadingRow(null); setRows(data.data); setIsLoading(false); }) @@ -50,30 +52,45 @@ const UserManagement = () => { function sendlink(nickname, formType) { API.users.invite({ - nickname + nickname, + formType: ""+formType, }) .then((values) => { let sendLink = window.location.origin + '/ui/register?t='+formType+'&nickname='+nickname+'&key=' + values.data.registerKey; - setToAction({...values.data, nickname, sendLink, formType}); + setToAction({...values.data, nickname, sendLink, formType, formAction: formType === 2 ? 'invite them to the server' : 'let them reset their password'}); setOpenInviteForm(true); }); } return <> + {openInviteForm ? setOpenInviteForm(false)}> - Invite User - Send this link to {toAction.nickname} to invite them to the system: +
+ {toAction.emailWasSent ? +
+ An email has been sent with a link to {toAction.formAction}. Alternatively you can also share the link below: +
: +
+ Send this link to {toAction.nickname} to {toAction.formAction}: +
+ } +
+
+
{toAction.sendLink}
{ navigator.clipboard.writeText(toAction.sendLink); } }> -
{toAction.sendLink}
+
@@ -215,26 +232,34 @@ const UserManagement = () => { const isRegistered = new Date(r.registeredAt).getTime() > 0; const inviteExpired = new Date(r.registerKeyExp).getTime() < new Date().getTime(); + if (loadingRow === r.nickname) { + return
+ } + return <>{isRegistered ? () : () }