Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/native password generator #18

Merged
merged 2 commits into from
Oct 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"devDependencies": {
"@tauri-apps/cli": "^1.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.25.0",
"eslint": "^8.26.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.1.1", features = ["api-all"] }
open = "3.0.3"
rand = "0.8.5"
unicode-segmentation = "1.10.0"

[features]
# by default Tauri runs in production mode
Expand Down
82 changes: 65 additions & 17 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,81 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]

use rand::Rng;
use std::fs;
use unicode_segmentation::UnicodeSegmentation;

fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![open_website, save_string_to_disk])
.run(tauri::generate_context!())
.expect("error while running tauri application");
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
open_website,
save_string_to_disk,
generate_passwords
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

#[tauri::command]
fn open_website(website: &str) -> Result<String, String> {
match open::that(website) {
Ok(_) => {
Ok(String::from("Success"))
match open::that(website) {
Ok(_) => Ok(String::from("Success")),
Err(e) => Err(e.to_string()),
}
Err(e) => {
Err(e.to_string())
}
}
}

#[tauri::command]
fn save_string_to_disk(content: &str, path: &str) -> Result<String, String> {
match fs::write(path, content) {
Ok(_) => Ok(String::from("Success")),
Err(e) => Err(e.to_string()),
}
match fs::write(path, content) {
Ok(_) => Ok(String::from("Success")),
Err(e) => Err(e.to_string()),
}
}

#[tauri::command]
async fn generate_passwords(
min_length: u64,
max_length: u64,
character_set: &str,
amount: u64,
allow_duplicates: bool,
) -> Result<Vec<String>, String> {
let mut password_list: Vec<String> = Vec::new();
let mut max_count: f64 = 0.0;
let char_count = character_set.graphemes(true).count();

if !allow_duplicates {
let mut current = min_length;
while current <= max_length {
max_count += (char_count as f64).powf(current as f64);
current += 1;
}
}

let mut rng = rand::thread_rng();
let chars = character_set.chars();
for _n in 0..amount {
let mut can_continue = false;
while !can_continue {
let mut password = String::from("");
let length = rng.gen_range(min_length..(max_length + 1));
for _j in 0..length {
let index = rng.gen_range(0..char_count);
password.push(chars.clone().nth(index).unwrap());
}

if allow_duplicates || (!allow_duplicates && !password_list.contains(&password)) {
password_list.push(password);
can_continue = true;
}

if !can_continue && !allow_duplicates && password_list.len() as f64 == max_count {
return Ok(password_list);
}
}
}

Ok(password_list)
}
29 changes: 16 additions & 13 deletions src/components/App/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import ThemeSelector from '../../utils/ThemeSelector';
import TopBar from '../TopBar';
import ClippedDrawer from '../ClippedDrawer';
import UpdateDialog from '../UpdateDialog';
import { openWebSite, setError, setUpdate } from '../../reducers/MainReducer/Actions';
import {
openWebSite, setError, setLoading, setUpdate,
} from '../../reducers/MainReducer/Actions';
import Updater from '../../utils/Updater';
import AlertDialog from '../AlertDialog';
import packageJson from '../../../package.json';
Expand All @@ -33,10 +35,9 @@ const NotFound = lazy(() => import('../../routes/NotFound'));
const App = () => {
const [state, d1] = useContext(MainContext);
const {
themeIndex, themeType, update, languageIndex, autoUpdate, error,
themeIndex, themeType, update, languageIndex, autoUpdate, error, loading,
} = state;

const [loading, setLoading] = useState(false);
const [snackOpen, setSnackOpen] = useState(false);
const language = state.languages[languageIndex];

Expand Down Expand Up @@ -82,7 +83,7 @@ const App = () => {
d1(setError(e));
})
.finally(() => {
setLoading(false);
d1(setLoading(false));
});
};

Expand Down Expand Up @@ -121,15 +122,17 @@ const App = () => {
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
<Toolbar />
<Suspense fallback={<LoadingBar />}>
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/advanced" element={<Advanced />} />
<Route exact path="/generate" element={<Generate />} />
<Route exact path="/advisor" element={<Advisor />} />
<Route exact path="/settings" element={<Settings />} />
<Route exact path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
{loading ? <LoadingBar /> : (
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/advanced" element={<Advanced />} />
<Route exact path="/generate" element={<Generate />} />
<Route exact path="/advisor" element={<Advisor />} />
<Route exact path="/settings" element={<Settings />} />
<Route exact path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
)}
</Suspense>
</Box>
</Box>
Expand Down
1 change: 1 addition & 0 deletions src/contexts/MainContextProvider/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const initState = {
update: null,
error: null,
languageSelector,
loading: false,
};

export const MainContext = createContext(initState);
Expand Down
1 change: 1 addition & 0 deletions src/reducers/MainReducer/Actions/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export const SET_AUTO_UPDATE = 'SET_AUTO_UPDATE';
export const SET_UPDATE = 'SET_UPDATE';
export const SET_ERROR = 'SET_ERROR';
export const SET_LANGUAGE_SELECTOR = 'SET_LANGUAGE_SELECTOR';
export const SET_LOADING = 'SET_LOADING';
6 changes: 6 additions & 0 deletions src/reducers/MainReducer/Actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
SET_ERROR,
SET_LANGUAGE_INDEX,
SET_LANGUAGE_SELECTOR,
SET_LOADING,
SET_PAGE_INDEX,
SET_THEME_INDEX,
SET_THEME_TYPE,
Expand Down Expand Up @@ -67,3 +68,8 @@ export const setLanguageSelector = (value) => ({
type: SET_LANGUAGE_SELECTOR,
payload: value,
});

export const setLoading = (value) => ({
type: SET_LOADING,
payload: value,
});
6 changes: 6 additions & 0 deletions src/reducers/MainReducer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
SET_ERROR,
SET_LANGUAGE_INDEX,
SET_LANGUAGE_SELECTOR,
SET_LOADING,
SET_PAGE_INDEX,
SET_THEME_INDEX,
SET_THEME_TYPE,
Expand Down Expand Up @@ -65,6 +66,11 @@ const MainReducer = (state, action) => {
...state,
languageSelector: action.payload,
};
case SET_LOADING:
return {
...state,
loading: action.payload,
};
case SET_ERROR:
return {
...state,
Expand Down
16 changes: 16 additions & 0 deletions src/reducers/PasswordReducer/Actions/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { invoke } from '@tauri-apps/api';
import {
SET_ALLOW_DUPLICATES,
SET_BRACKETS,
Expand Down Expand Up @@ -78,3 +79,18 @@ export const setAllowDuplicates = (value) => ({
type: SET_ALLOW_DUPLICATES,
payload: value,
});

// eslint-disable-next-line max-len
export const generatePasswordArray = (min, max, characterSet, amount, allowDuplicates, worker) => {
// eslint-disable-next-line no-underscore-dangle
if (window.__TAURI__) {
return invoke('generate_passwords', {
minLength: parseFloat(min),
maxLength: parseFloat(max),
characterSet,
amount: parseFloat(amount),
allowDuplicates,
});
}
return worker.PasswordGenerator(min, max, characterSet, amount, allowDuplicates);
};
3 changes: 3 additions & 0 deletions src/reducers/PasswordReducer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import {
} from './Actions/actionTypes';

const PasswordReducer = (state, action) => {
if (!action || !action.type) {
return state;
}
switch (action.type) {
case SET_PASSWORD_LENGTH_MIN:
if (parseFloat(action.payload) < 1) return state;
Expand Down
13 changes: 9 additions & 4 deletions src/routes/Advanced/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import FormGroup from '@mui/material/FormGroup';
import { createWorkerFactory, useWorker } from '@shopify/react-web-worker';
import { setError, setPageIndex } from '../../reducers/MainReducer/Actions';
import { setError, setLoading, setPageIndex } from '../../reducers/MainReducer/Actions';
import { MainContext } from '../../contexts/MainContextProvider';
import { PasswordContext } from '../../contexts/PasswordContextProvider';
import {
generatePasswordArray,
setAllowDuplicates,
setCharacterSet, setPasswords,
setUseAdvanced,
Expand Down Expand Up @@ -67,13 +68,17 @@ const Advanced = () => {
return;
}

worker.PasswordGenerator(min, max, simpleCharacterSet, amount, allowDuplicates)
d1(setLoading(true));
generatePasswordArray(min, max, simpleCharacterSet, amount, allowDuplicates, worker)
.then((res) => {
d2(setPasswords(res));
navigate('/generate');
})
.catch((e) => {
d1(setError(e));
.catch((err) => {
d1(setError(err));
})
.finally(() => {
d1(setLoading(false));
});
};

Expand Down
14 changes: 9 additions & 5 deletions src/routes/Generate/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { save } from '@tauri-apps/api/dialog';
import { invoke } from '@tauri-apps/api/tauri';
import { MainContext } from '../../contexts/MainContextProvider';
import { PasswordContext } from '../../contexts/PasswordContextProvider';
import { setError, setPageIndex } from '../../reducers/MainReducer/Actions';
import { setError, setLoading, setPageIndex } from '../../reducers/MainReducer/Actions';
import PasswordStrength from '../../utils/PasswordStrength';
import { setPasswords } from '../../reducers/PasswordReducer/Actions';
import { generatePasswordArray, setPasswords } from '../../reducers/PasswordReducer/Actions';
import MuiVirtualizedTable from '../../components/MuiVirtualizedTable';
import ExportDialog from '../../components/ExportDialog';

Expand Down Expand Up @@ -104,12 +104,16 @@ const Generate = () => {
return;
}

worker.PasswordGenerator(min, max, simpleCharacterSet, amount, allowDuplicates)
d1(setLoading(true));
generatePasswordArray(min, max, simpleCharacterSet, amount, allowDuplicates, worker)
.then((res) => {
d2(setPasswords(res));
})
.catch((e) => {
d1(setError(e));
.catch((err) => {
d1(setError(err));
})
.finally(() => {
d1(setLoading(false));
});
};

Expand Down
13 changes: 9 additions & 4 deletions src/routes/Home/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import TextField from '@mui/material/TextField';
import { useNavigate } from 'react-router-dom';
import { createWorkerFactory, useWorker } from '@shopify/react-web-worker';
import { MainContext } from '../../contexts/MainContextProvider';
import { setError, setPageIndex } from '../../reducers/MainReducer/Actions';
import { setError, setLoading, setPageIndex } from '../../reducers/MainReducer/Actions';
import { PasswordContext } from '../../contexts/PasswordContextProvider';
import {
generatePasswordArray,
setBrackets,
setCapitalLetters,
setNumbers,
Expand Down Expand Up @@ -83,13 +84,17 @@ const Home = () => {
return;
}

worker.PasswordGenerator(min, max, simpleCharacterSet, amount, allowDuplicates)
d1(setLoading(true));
generatePasswordArray(min, max, simpleCharacterSet, amount, allowDuplicates, worker)
.then((res) => {
d2(setPasswords(res));
navigate('/generate');
})
.catch((e) => {
d1(setError(e));
.catch((err) => {
d1(setError(err));
})
.finally(() => {
d1(setLoading(false));
});
};

Expand Down
2 changes: 1 addition & 1 deletion src/utils/PasswordGenerator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function PasswordGenerator(minLength, maxLength, characterSet, amount, al
// We've reached the end of the line
if (canContinue === false && allowDuplicates === false
&& passwordArray.length === maxCount) {
break;
return passwordArray;
}
}
}
Expand Down
Loading