Password update
This commit is contained in:
parent
48252de9f3
commit
db33be9a37
34
main.go
34
main.go
@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
const VERSION = "v10";
|
const VERSION = "v10";
|
||||||
const LISTEN_HOST = "0.0.0.0";
|
const LISTEN_HOST = "0.0.0.0";
|
||||||
const DB_SCHEMA = 12
|
const DB_SCHEMA = 13
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagPort int
|
flagPort int
|
||||||
@ -68,6 +68,7 @@ func main() {// {{{
|
|||||||
http.HandleFunc("/session/create", sessionCreate)
|
http.HandleFunc("/session/create", sessionCreate)
|
||||||
http.HandleFunc("/session/retrieve", sessionRetrieve)
|
http.HandleFunc("/session/retrieve", sessionRetrieve)
|
||||||
http.HandleFunc("/session/authenticate", sessionAuthenticate)
|
http.HandleFunc("/session/authenticate", sessionAuthenticate)
|
||||||
|
http.HandleFunc("/user/password", userPassword)
|
||||||
http.HandleFunc("/node/tree", nodeTree)
|
http.HandleFunc("/node/tree", nodeTree)
|
||||||
http.HandleFunc("/node/retrieve", nodeRetrieve)
|
http.HandleFunc("/node/retrieve", nodeRetrieve)
|
||||||
http.HandleFunc("/node/create", nodeCreate)
|
http.HandleFunc("/node/create", nodeCreate)
|
||||||
@ -201,6 +202,37 @@ func sessionAuthenticate(w http.ResponseWriter, r *http.Request) {// {{{
|
|||||||
})
|
})
|
||||||
}// }}}
|
}// }}}
|
||||||
|
|
||||||
|
func userPassword(w http.ResponseWriter, r *http.Request) {// {{{
|
||||||
|
var err error
|
||||||
|
var ok bool
|
||||||
|
var session Session
|
||||||
|
|
||||||
|
if session, _, err = ValidateSession(r, true); err != nil {
|
||||||
|
responseError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := struct {
|
||||||
|
CurrentPassword string
|
||||||
|
NewPassword string
|
||||||
|
}{}
|
||||||
|
if err = parseRequest(r, &req); err != nil {
|
||||||
|
responseError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err = session.UpdatePassword(req.CurrentPassword, req.NewPassword)
|
||||||
|
if err != nil {
|
||||||
|
responseError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData(w, map[string]interface{}{
|
||||||
|
"OK": true,
|
||||||
|
"CurrentPasswordOK": ok,
|
||||||
|
})
|
||||||
|
}// }}}
|
||||||
|
|
||||||
func nodeTree(w http.ResponseWriter, r *http.Request) {// {{{
|
func nodeTree(w http.ResponseWriter, r *http.Request) {// {{{
|
||||||
var err error
|
var err error
|
||||||
var session Session
|
var session Session
|
||||||
|
@ -541,6 +541,19 @@ header .menu {
|
|||||||
#app.node.toggle-tree #tree {
|
#app.node.toggle-tree #tree {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
#profile-settings {
|
||||||
|
color: #333;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
#profile-settings .passwords {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content 200px;
|
||||||
|
grid-gap: 8px 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
#profile-settings .passwords div {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
@media only screen and (max-width: 932px) {
|
@media only screen and (max-width: 932px) {
|
||||||
#app.node {
|
#app.node {
|
||||||
grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "files" "blank";
|
grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "files" "blank";
|
||||||
|
@ -86,6 +86,10 @@ export class NodeUI extends Component {
|
|||||||
page = html`<${Keys} nodeui=${this} />`
|
page = html`<${Keys} nodeui=${this} />`
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'profile-settings':
|
||||||
|
page = html`<${ProfileSettings} nodeui=${this} />`
|
||||||
|
break
|
||||||
|
|
||||||
case 'search':
|
case 'search':
|
||||||
page = html`<${Search} nodeui=${this} />`
|
page = html`<${Search} nodeui=${this} />`
|
||||||
break
|
break
|
||||||
@ -100,6 +104,7 @@ export class NodeUI extends Component {
|
|||||||
<header class="${modified}" onclick=${()=>this.saveNode()}>
|
<header class="${modified}" onclick=${()=>this.saveNode()}>
|
||||||
<div class="tree"><img src="/images/${window._VERSION}/tree.svg" onclick=${()=>document.getElementById('app').classList.toggle('toggle-tree')} /></div>
|
<div class="tree"><img src="/images/${window._VERSION}/tree.svg" onclick=${()=>document.getElementById('app').classList.toggle('toggle-tree')} /></div>
|
||||||
<div class="name">Notes</div>
|
<div class="name">Notes</div>
|
||||||
|
<div class="search" onclick=${evt=>{ evt.stopPropagation(); this.showPage('search')}}><img src="/images/${window._VERSION}/search.svg" /></div>
|
||||||
<div class="add" onclick=${evt=>this.createNode(evt)}><img src="/images/${window._VERSION}/add.svg" /></div>
|
<div class="add" onclick=${evt=>this.createNode(evt)}><img src="/images/${window._VERSION}/add.svg" /></div>
|
||||||
<div class="keys" onclick=${evt=>{ evt.stopPropagation(); this.showPage('keys')}}><img src="/images/${window._VERSION}/padlock.svg" /></div>
|
<div class="keys" onclick=${evt=>{ evt.stopPropagation(); this.showPage('keys')}}><img src="/images/${window._VERSION}/padlock.svg" /></div>
|
||||||
<div class="menu" onclick=${evt=>this.showMenu(evt)}>☰</div>
|
<div class="menu" onclick=${evt=>this.showMenu(evt)}>☰</div>
|
||||||
@ -213,7 +218,11 @@ export class NodeUI extends Component {
|
|||||||
this.node.value.create(name, nodeID=>this.goToNode(nodeID))
|
this.node.value.create(name, nodeID=>this.goToNode(nodeID))
|
||||||
}//}}}
|
}//}}}
|
||||||
saveNode() {//{{{
|
saveNode() {//{{{
|
||||||
let content = this.nodeContent.current.contentDiv.current.value
|
let nodeContent = this.nodeContent.current
|
||||||
|
if(this.page.value != 'node' || nodeContent === null)
|
||||||
|
return
|
||||||
|
|
||||||
|
let content = nodeContent.contentDiv.current.value
|
||||||
this.node.value.setContent(content)
|
this.node.value.setContent(content)
|
||||||
this.node.value.save(()=>this.props.app.nodeModified.value = false)
|
this.node.value.save(()=>this.props.app.nodeModified.value = false)
|
||||||
}//}}}
|
}//}}}
|
||||||
@ -547,10 +556,14 @@ class Menu extends Component {
|
|||||||
return html`
|
return html`
|
||||||
<div id="blackout" onclick=${()=>nodeui.menu.value = false}></div>
|
<div id="blackout" onclick=${()=>nodeui.menu.value = false}></div>
|
||||||
<div id="menu">
|
<div id="menu">
|
||||||
|
<div class="section">Current note</div>
|
||||||
<div class="item" onclick=${()=>{ nodeui.renameNode(); nodeui.menu.value = false }}>Rename</div>
|
<div class="item" onclick=${()=>{ nodeui.renameNode(); nodeui.menu.value = false }}>Rename</div>
|
||||||
<div class="item" onclick=${()=>{ nodeui.deleteNode(); nodeui.menu.value = false }}>Delete</div>
|
<div class="item" onclick=${()=>{ nodeui.upload.value = true; nodeui.menu.value = false }}>Upload</div>
|
||||||
<div class="item separator" onclick=${()=>{ nodeui.showPage('properties'); nodeui.menu.value = false }}>Properties</div>
|
<div class="item " onclick=${()=>{ nodeui.showPage('node-properties'); nodeui.menu.value = false }}>Properties</div>
|
||||||
<div class="item separator" onclick=${()=>{ nodeui.upload.value = true; nodeui.menu.value = false }}>Upload</div>
|
<div class="item separator" onclick=${()=>{ nodeui.deleteNode(); nodeui.menu.value = false }}>Delete</div>
|
||||||
|
|
||||||
|
<div class="section">User</div>
|
||||||
|
<div class="item" onclick=${()=>{ nodeui.showPage('profile-settings'); nodeui.menu.value = false }}>Settings</div>
|
||||||
<div class="item" onclick=${()=>{ nodeui.logout(); nodeui.menu.value = false }}>Log out</div>
|
<div class="item" onclick=${()=>{ nodeui.logout(); nodeui.menu.value = false }}>Log out</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@ -788,4 +801,77 @@ class Search extends Component {
|
|||||||
}//}}}
|
}//}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ProfileSettings extends Component {
|
||||||
|
render({ nodeui }, {}) {//{{{
|
||||||
|
return html`
|
||||||
|
<div id="profile-settings">
|
||||||
|
<h1>User settings</h1>
|
||||||
|
|
||||||
|
<h2>Password</h2>
|
||||||
|
<div class="passwords">
|
||||||
|
<div>Current</div>
|
||||||
|
<input type="password" id="current-password" placeholder="Current password" onkeydown=${evt=>this.keyHandler(evt)} />
|
||||||
|
|
||||||
|
<div>New</div>
|
||||||
|
<input type="password" id="new-password1" placeholder="Password" onkeydown=${evt=>this.keyHandler(evt)} />
|
||||||
|
|
||||||
|
<div>Repeat</div>
|
||||||
|
<input type="password" id="new-password2" placeholder="Repeat password" onkeydown=${evt=>this.keyHandler(evt)} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick=${()=>this.updatePassword()}>Change password</button>
|
||||||
|
</div>`
|
||||||
|
}//}}}
|
||||||
|
componentDidMount() {//{{{
|
||||||
|
document.getElementById('current-password').focus()
|
||||||
|
}//}}}
|
||||||
|
|
||||||
|
keyHandler(evt) {//{{{
|
||||||
|
let handled = true
|
||||||
|
|
||||||
|
switch(evt.key.toUpperCase()) {
|
||||||
|
case 'ENTER':
|
||||||
|
this.updatePassword()
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
handled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if(handled) {
|
||||||
|
evt.preventDefault()
|
||||||
|
evt.stopPropagation()
|
||||||
|
}
|
||||||
|
}//}}}
|
||||||
|
updatePassword() {//{{{
|
||||||
|
let curr_pass = document.getElementById('current-password').value
|
||||||
|
let pass1 = document.getElementById('new-password1').value
|
||||||
|
let pass2 = document.getElementById('new-password2').value
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(pass1.length < 4) {
|
||||||
|
throw new Error('Password has to be at least 4 characters long')
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pass1 != pass2) {
|
||||||
|
throw new Error(`Passwords don't match`)
|
||||||
|
}
|
||||||
|
|
||||||
|
window._app.current.request('/user/password', {
|
||||||
|
CurrentPassword: curr_pass,
|
||||||
|
NewPassword: pass1,
|
||||||
|
})
|
||||||
|
.then(res=>{
|
||||||
|
if(res.CurrentPasswordOK)
|
||||||
|
alert('Password is changed successfully')
|
||||||
|
else
|
||||||
|
alert('Current password is invalid')
|
||||||
|
})
|
||||||
|
} catch(err) {
|
||||||
|
alert(err.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}//}}}
|
||||||
|
}
|
||||||
|
|
||||||
// vim: foldmethod=marker
|
// vim: foldmethod=marker
|
||||||
|
@ -615,6 +615,22 @@ header {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#profile-settings {
|
||||||
|
color: #333;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.passwords {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content 200px;
|
||||||
|
grid-gap: 8px 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
div {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 932px) {
|
@media only screen and (max-width: 932px) {
|
||||||
#app.node {
|
#app.node {
|
||||||
.layout-crumbs();
|
.layout-crumbs();
|
||||||
|
37
user.go
Normal file
37
user.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// Standard
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (session Session) UpdatePassword(currPass, newPass string) (ok bool, err error) {
|
||||||
|
var result sql.Result
|
||||||
|
var rowsAffected int64
|
||||||
|
|
||||||
|
result, err = db.Exec(`
|
||||||
|
UPDATE public.user
|
||||||
|
SET
|
||||||
|
password = password_hash(
|
||||||
|
/* salt in hex */
|
||||||
|
ENCODE(gen_random_bytes(16), 'hex'),
|
||||||
|
|
||||||
|
/* password */
|
||||||
|
$1::bytea
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
id = $2 AND
|
||||||
|
password=password_hash(SUBSTRING(password FROM 1 FOR 32), $3::bytea)
|
||||||
|
RETURNING id
|
||||||
|
`,
|
||||||
|
newPass,
|
||||||
|
session.UserID,
|
||||||
|
currPass,
|
||||||
|
)
|
||||||
|
|
||||||
|
if rowsAffected, err = result.RowsAffected(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowsAffected > 0, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user