Skip to content

Commit

Permalink
Version 1.0.1
Browse files Browse the repository at this point in the history
Update dependencies
Update README.md
Add config options
- Show password
- Force all character types
- Use legacy alphabet
- Password length
  • Loading branch information
cout970 committed Oct 16, 2021
1 parent f6d3a32 commit e3efae6
Show file tree
Hide file tree
Showing 8 changed files with 6,622 additions and 6,458 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
react-with-typescript
# SSS

Password manager based on name hashes instead on storage

[Try the online version](https://sss.pagoru.es/)
12,502 changes: 6,225 additions & 6,277 deletions package-lock.json

Large diffs are not rendered by default.

33 changes: 17 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
{
"name": "sss.pagoru.es",
"version": "1.0.0",
"version": "1.0.1",
"description": "",
"main": "src/index.tsx",
"scripts": {
"build": "./node_modules/.bin/webpack -p",
"start": "./node_modules/.bin/webpack-dev-server --inline --hot --history-api-fallback --open --content-base ./public"
"start_": "./node_modules/.bin/webpack-dev-server --hot --history-api-fallback --open --content-base ./public",
"start": "npx webpack serve --mode development"
},
"author": "pablo",
"license": "MIT",
"devDependencies": {
"@types/react": "^16.3.11",
"@types/react-dom": "^16.0.5",
"awesome-typescript-loader": "^3.5.0",
"css-loader": "^0.28.11",
"less": "^2.7.3",
"@types/react": "^17.0.30",
"@types/react-dom": "^17.0.9",
"css-loader": "^6.4.0",
"less-loader": "^4.1.0",
"qrcode": "^1.2.0",
"react-copy-to-clipboard": "^5.0.1",
"style-loader": "^0.19.1",
"super-secret-settings": "^3.0.2",
"typescript": "^2.8.1",
"webpack": "^3.11.0",
"webpack-dev-server": "^2.11.2"
"style-loader": "^3.3.0",
"ts-loader": "^9.2.6",
"webpack": "^5.58.2",
"webpack-cli": "^4.9.0",
"webpack-dev-server": "^4.3.1"
},
"dependencies": {
"react": "^16.3.2",
"react-dom": "^16.3.2"
"buffer": "^6.0.3",
"random-seed": "^0.3.0",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.0.4",
"react-dom": "^17.0.2",
"super-secret-settings": "file:../super-secret-settings"
}
}
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta id="viewport" name="viewport" content="width=320; user-scalable=no; initial-scale=1.0;"/>
<link id="favicon" rel="shortcut icon" type="image/png" href="https://robohash.org/Bender"/>
<title>sss</title>
<title>SSS</title>
</head>
<body>
<main id="robot">
Expand Down
253 changes: 201 additions & 52 deletions src/application.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,214 @@
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { CopyToClipboard } from 'react-copy-to-clipboard';
import './index.less'
import * as React from "react";
import {useEffect, useState} from "react";
import {CopyToClipboard} from 'react-copy-to-clipboard';
import {getHash, getPassword, getCssColor} from 'super-secret-settings';

import { getHash, getPassword } from 'super-secret-settings';
const main_img = 'https://robohash.org/Bender';

import './index.less'
export function Application() {
const [secret, setSecret] = useState('');
const [service, setService] = useState('');
const [openConfig, setOpenConfig] = useState(false);
const [configLoaded, setConfigLoaded] = useState(false);
const [lenght, setLenght] = useState(12);
const [forceAllChars, setForceAllChars] = useState(false);
const [legacyAlphabet, setLegacyAlphabet] = useState(true);
const [showPassword, setShowPassword] = useState(false);

const qr_url = 'https://api.qrserver.com/v1/create-qr-code/?size=500x500&data=';
const qr_url2 = 'https://loremflickr.com/500/500?lock=';
const qr_static = 'https://instagram.fmad7-1.fna.fbcdn.net/vp/40e87239ca4b09b3d13fe05a553df631/5B65842E/t51.2885-15/e35/30602671_269685373571513_4681094377852895232_n.jpg';
useEffect(() => {
try {
// If cookies are disable this will crash, there is no way to detect it :(
if (configLoaded) {
window.localStorage.setItem('sss.password_length', `${lenght}`);
window.localStorage.setItem('sss.force_all_chars', `${forceAllChars}`);
window.localStorage.setItem('sss.legacy_alphabet', `${legacyAlphabet}`);
window.localStorage.setItem('sss.show_password', `${showPassword}`);
} else {
let password_length = window.localStorage.getItem('sss.password_length') || '14';
let force_all_chars = window.localStorage.getItem('sss.force_all_chars') || 'false';
let legacy_alphabet = window.localStorage.getItem('sss.legacy_alphabet') || 'true';
let show_password = window.localStorage.getItem('sss.show_password') || 'true';

export default class Application extends React.Component<{}, {}> {
setLenght(+password_length);
setForceAllChars(force_all_chars == 'true');
setLegacyAlphabet(legacy_alphabet == 'true');
setShowPassword(show_password == 'true');
setConfigLoaded(true);
}
} catch (e) {
console.error(e);
}
});

constructor(props, context?: any){
super(props, context);
const alphabet = legacyAlphabet
? 'ABCDFGHIJKLMNOPQRSTUVWXYZabdfghijklmnopqrstuvwxyz1234567890'
: 'ABCDEFGHIJKLMNÑOPQRSTUVWXYZabcdefghijklmnñopqrstuvwxyz1234567890!@#$%*()_+=-€¡?¿[]{}",./ç<>| ';

this.state = {
secret: '',
service: '',
hash: '00000000',
password: ''
};
let password = getPassword(secret, service, lenght, alphabet);

this.onChangeSecret = this.onChangeSecret.bind(this);
this.onChangeService = this.onChangeService.bind(this);
}
if (!hasAllChars(password, !legacyAlphabet)) {
console.log(password);
}

onChangeSecret(event: any) {
const secret = event.target.value;
this.setState({
secret: secret,
hash: getHash(secret).substr(0, 8),
password: getPassword(secret, this.state['service'])
});
if (forceAllChars && password.length >= 4) {
while (!hasAllChars(password, !legacyAlphabet)) {
password = getPassword(secret, password, lenght, alphabet);
}
}

onChangeService(event: any) {
const service = event.target.value;
this.setState({
service: service,
hash: getHash(this.state['secret']).substr(0, 8),
password: getPassword(this.state['secret'], service)
});
}
const secretHash = getHash(secret).substr(0, 8);
const serviceHash = getHash(service).substr(0, 8);
const passwordHash = getHash(password).substr(0, 8);

render() {
document.body.style.backgroundColor = `#${this.state['hash'].substr(0, 6)}`;
return (
<div className="robot">
<div className="inputs">
<input id="secret" placeholder="secret" type="password" onChange={this.onChangeSecret} autoFocus/>
<input id="service" placeholder="service" value={this.state['service']} onChange={this.onChangeService}/>
<div>
</div>
<label id="hexadecimalSeed" className="noselect">{this.state['hash']}</label>
</div>
<CopyToClipboard text={this.state['password']}>
<img id="robotRock" src={qr_static}/>
</CopyToClipboard>
</div>
);
}
const secretColor = secret ? getCssColor(secret, 50, 80) : 'white';
const serviceColor = service ? getCssColor(service, 50, 80) : 'white';
const passwordColor = secret || service ? getCssColor(password) : '#249D9F';

const changeSecret = (event) => setSecret(event.target.value);
const changeService = (event) => setService(event.target.value);
const setPassLength = (text) => {
let int = +text;
if (isNaN(int) || int < 0 || int > 999) return;
setLenght(int);
}

const toggleConfig = () => setOpenConfig(!openConfig);

document.body.style.backgroundColor = passwordColor;

return (
<div className="robot">
<CopyToClipboard text={password}>
<img id="robot-img" src={main_img} alt=""/>
</CopyToClipboard>

<div className="inputs">
<div className='input-wrapper'>
<input id="secret"
placeholder="secret"
type="password"
value={secret}
style={{backgroundColor: secretColor}}
onChange={changeSecret}
autoFocus/>

<label className="hexadecimal-seed noselect" title='Check hash'>
{secretHash}
</label>
</div>

<div className='input-wrapper'>
<input id="service"
placeholder="service"
value={service}
style={{backgroundColor: serviceColor}}
onChange={changeService}/>
<div/>
<label className="hexadecimal-seed noselect" title='Check hash'>
{serviceHash}
</label>
</div>

<div className='input-wrapper'>
<input id="password-output"
disabled={true}
placeholder="password"
type={showPassword ? 'text' : 'password'}
style={{backgroundColor: 'white'}}
value={secret || service ? password : ''}
onChange={() => null}/>
<div/>
<label className="hexadecimal-seed noselect" title='Check hash'>
{secret || service ? passwordHash : ''}
</label>
</div>
</div>

<div className='last-row'>
<CopyToClipboard text={secret.length ? password : ''}>
<button className={secret.length ? 'copy-btn' : 'copy-btn disable'}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
className="bi bi-clipboard" viewBox="0 0 16 16">
<path
d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
<path
d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
</svg>
<div>Copy to clipboard</div>
</button>
</CopyToClipboard>

<button className='config' onClick={toggleConfig}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
className="bi bi-gear-fill" viewBox="0 0 16 16">
<path
d="M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .872 2.105l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.105-.872l-.1-.34zM8 10.93a2.929 2.929 0 1 1 0-5.86 2.929 2.929 0 0 1 0 5.858z"/>
</svg>
</button>
</div>

{
openConfig &&
<div className='config-panel'>

<div className='field'>
<label>
<input type="checkbox"
checked={showPassword}
onChange={() => setShowPassword(!showPassword)}/>
Show password
</label>
</div>

<div className='field'>
<label>
<input type="checkbox"
checked={forceAllChars}
onChange={() => setForceAllChars(!forceAllChars)}/>
Force all character types
</label>
</div>

<div className='field'>
<label>
<input type="checkbox"
checked={legacyAlphabet}
onChange={() => setLegacyAlphabet(!legacyAlphabet)}/>
Use legacy alphabet
</label>
</div>

<div className='field'>
<label>
<input type="number"
min={1}
max={512}
step={1}
value={lenght}
onChange={e => setPassLength(e.target.value)}/>
Password length
</label>

</div>
</div>
}
</div>
);
}

function hasAllChars(text, includeSpecial) {
let lower = false;
let upper = false;
let number = false;
let special = false;

text.split('').forEach(char => {
lower = lower || /[a-z]/.test(char);
upper = upper || /[A-Z]/.test(char);
number = number || /\d/.test(char);
special = special || /[^A-Za-z0-9]/.test(char);
});

return lower && upper && number && (!includeSpecial || special);
}
Loading

0 comments on commit e3efae6

Please sign in to comment.