Skip to content

Commit

Permalink
add inband password change (XEP-0077) (fix #178)
Browse files Browse the repository at this point in the history
  • Loading branch information
sualko committed Jul 22, 2018
1 parent ecad10c commit ad00d41
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 2 deletions.
18 changes: 17 additions & 1 deletion src/connection/AbstractConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ abstract class AbstractConnection {
}

public queryArchive(archive: JID, queryId: string, beforeResultId?: string, end?: Date): Promise<Element> {
var iq = $iq({
let iq = $iq({
type: 'set'
});

Expand Down Expand Up @@ -244,6 +244,22 @@ abstract class AbstractConnection {
return this.sendIQ(iq);
}

public changePassword(newPassword: string): Promise<Element> {
let iq = $iq({
type: 'set'
});

iq.c('query', {
xmlns: 'jabber:iq:register'
});

iq.c('username').t(this.getJID().node).up();

iq.c('password').t(newPassword);

return this.sendIQ(iq);
}

public close() {

}
Expand Down
109 changes: 108 additions & 1 deletion src/ui/dialogs/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import List from '../DialogList'
import ListItem from '../DialogListItem'
import AvatarSet from '../AvatarSet'
import Translation from '../../util/Translation';
import Log from '../../util/Log'

const ENOUGH_BITS_OF_ENTROPY = 50;

export default function() {
let dialog = new Dialog('', false, 'settings');
Expand Down Expand Up @@ -91,7 +94,7 @@ class AccountPage extends Page {
}
}

//@TODO priorities? Are they still needed/used?
//@REVIEW priorities? Are they still needed/used?
class ConnectionSection extends Section {
constructor(navigation: Navigation, private account) {
super(navigation, 'Connection');
Expand All @@ -101,14 +104,118 @@ class ConnectionSection extends Section {
let jid = this.account.getJID();
let contentElement = new List();

let changePasswordActionHandler = () => this.navigation.goTo(new PasswordPage(this.navigation, this.account));

contentElement.append(new ListItem('Jabber ID', jid.bare));
contentElement.append(new ListItem('Resource', jid.resource));
contentElement.append(new ListItem('BOSH url', this.account.getConnectionUrl()));
contentElement.append(new ListItem('Change password', undefined, changePasswordActionHandler));

return contentElement.getDOM();
}
}

class PasswordPage extends Page {
constructor(navigation: Navigation, private account) {
super(navigation, 'Password');
}

protected generateContentElement(): JQuery {
//@REVIEW maybe template
let contentElement = $('<form>');
contentElement.addClass('form-horizontal');
contentElement.css('marginTop', '30px'); //@REVIEW

let explanationElement = $(`<p class="jsxc-explanation">{{t "password_explanation"}}</p>`)

let passwordAElement = $(`<div class="form-group">
<label class="col-sm-4 control-label" for="jsxc-password-A">Password</label>
<div class="col-sm-8">
<input type="password" name="password-A" id="jsxc-password-A" class="form-control" required="required">
<p class="jsxc-inputinfo"></p>
</div>
</div>`);

let passwordBElement = $(`<div class="form-group">
<label class="col-sm-4 control-label" for="jsxc-password-B">Control</label>
<div class="col-sm-8">
<input type="password" name="password-B" id="jsxc-password-B" class="form-control" required="required">
<p class="jsxc-inputinfo jsxc-hidden"></p>
</div>
</div>`);

let submitElement = $(`<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button disabled="disabled" class="jsxc-button jsxc-button--primary">Change password</button>
</div>
</div>`);

let errorElement = $(`<div class="jsxc-alert jsxc-alert--warning jsxc-hidden"></div>`);

contentElement.append(explanationElement);
contentElement.append(passwordAElement);
contentElement.append(passwordBElement);
contentElement.append(submitElement);
contentElement.append(errorElement);

passwordAElement.find('input').on('input', function() {
let value = <string>$(this).val();
let numberOfPossibleCharacters = 0;

if (/[a-z]/.test(value)) {
numberOfPossibleCharacters += 26
}

if (/[A-Z]/.test(value)) {
numberOfPossibleCharacters += 26
}

if (/[0-9]/.test(value)) {
numberOfPossibleCharacters += 10
}

if (/[^a-zA-Z0-9]/.test(value)) {
numberOfPossibleCharacters += 15 // most common
}

let entropy = Math.pow(numberOfPossibleCharacters, value.length);
let bitsOfEntropy = Math.log2(entropy);
let strength = Math.min(100, Math.round(bitsOfEntropy / ENOUGH_BITS_OF_ENTROPY * 100));

passwordAElement.find('.jsxc-inputinfo').text(`Strength: ${strength}%`);
});

contentElement.find('input').on('input', function() {
let passwordA = passwordAElement.find('input').val();
let passwordB = passwordBElement.find('input').val();

submitElement.find('button').prop('disabled', passwordA !== passwordB)
});

contentElement.submit((ev) => {
ev.preventDefault();

let passwordA = passwordAElement.find('input').val();
let passwordB = passwordBElement.find('input').val();

if (passwordA !== passwordB) {
return;
}

this.account.getConnection().changePassword(passwordA).then(() => {
Log.debug('Password was changed');
}).catch((errStanza) => {
//@TODO check for error 401 and form (https://xmpp.org/extensions/xep-0077.html#usecases-changepw)

errorElement.removeClass('jsxc-hidden');
errorElement.text('Server error. Password was not changed.');
});
});

return contentElement;
}
}

class PluginSection extends Section {
constructor(navigation: Navigation, private account) {
super(navigation, 'Plugins');
Expand Down

0 comments on commit ad00d41

Please sign in to comment.