// bitmask.js
// Copyright (C) 2016 LEAP
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
/**
* bitmask object
*
* Contains all the bitmask API mapped by sections
* - user. User management like login, creation, ...
* - mail. Email service control.
* - keys. Keyring operations.
* - events. For registering to events.
*
* Every function returns a Promise that will be triggered once the request is
* finished or will fail if there was any error. Errors are always user readable
* strings.
*/
try {
// Use Promises in non-ES6 compliant engines.
eval('import "babel-polyfill";')
}
catch (err) {}
var bitmask = function(){
var event_handlers = {};
var api_url = '/API/';
var api_token = null;
var last_uid = null;
var last_uuid = null;
if (window.location.protocol === "file:") {
api_url = 'http://localhost:7070/API/';
}
if (window.location.hash) {
api_token = window.location.hash.replace('#', '')
}
function call(command) {
var url = api_url + command.slice(0, 3).join('/');
var data = JSON.stringify(command.slice(3));
return new Promise(function(resolve, reject) {
var req = new XMLHttpRequest();
req.open('POST', url);
if (api_token) {
req.setRequestHeader("X-Bitmask-Auth", api_token)
}
req.onload = function() {
if (req.status == 200) {
parseResponse(req.response, resolve, reject);
}
else {
reject(Error(req.statusText));
}
};
req.onerror = function() {
reject(Error("Network Error"));
};
req.send(data);
});
};
function parseResponse(raw_response, resolve, reject) {
var response = JSON.parse(raw_response);
if (response.error === null) {
resolve(response.result);
} else {
reject(response.error);
}
};
function event_polling() {
if (api_token) {
call(['events', 'poll']).then(function(response) {
if (response !== null) {
var event = response[0];
var content = response[1];
if (event in event_handlers) {
Object.values(event_handlers[event]).forEach(function(handler) {
handler(event, content);
})
}
}
event_polling();
}, function(error) {
setTimeout(event_polling, 5000);
});
}
};
event_polling();
function private_str(priv) {
if (priv) {
return 'private'
}
return 'public'
};
return {
api_token: function() {return api_token},
core: {
/**
* Get bitmaskd version
*
* @return {Promise} {'version_core': str}
*/
version: function() {
return call(['core', 'version']);
},
/**
* Stop bitmaskd
*/
stop: function() {
return call(['core', 'stop']);
}
},
bonafide: {
provider: {
create: function(domain) {
return call(['bonafide', 'provider', 'create', domain]);
},
read: function(domain, service) {
if (typeof service !== 'string') {
service = "";
}
return call(['bonafide', 'provider', 'read', domain, service]);
},
delete: function(domain) {
return call(['bonafide', 'provider', 'delete', domain]);
},
list: function(seeded) {
if (typeof seeded !== 'boolean') {
seeded = false;
}
return call(['bonafide', 'provider', 'list', seeded]);
}
},
/**
* uids are of the form user@provider.net
*/
user: {
/**
* Register a new user
*
* @param {string} uid The uid to be created
* @param {string} password The user password
* @param {boolean} autoconf If the provider should be autoconfigured if it's not already known
* If it's not provided it will default to false
*/
create: function(uid, password, invite, autoconf) {
if (typeof autoconf !== 'boolean') {
autoconf = false;
}
return call(['bonafide', 'user', 'create', uid, password, invite, autoconf]);
},
/**
* Login
*
* @param {string} uid The uid to log in
* @param {string} password The user password
* @param {boolean} autoconf If the provider should be autoconfigured if it's not already known
* If it's not provided it will default to false
*/
auth: function(uid, password, autoconf) {
if (typeof autoconf !== 'boolean') {
autoconf = false;
}
return call(['bonafide', 'user', 'authenticate', uid, password, autoconf]).then(function(response) {
last_uuid = response.uuid
last_uid = uid
return response;
});
},
/**
* Logout
*
* @param {string} uid The uid to log out.
*/
logout: function(uid) {
return call(['bonafide', 'user', 'logout', uid]);
},
/**
* List users
*
* @return {Promise} [{'userid': str, 'authenticated': boolean}]
*/
list: function() {
return call(['bonafide', 'user', 'list']);
},
/**
* Change password
*
* @param {string} uid The uid to log in
* @param {string} current_password The current user password
* @param {string} new_password The new user password
*/
update: function(uid, current_password, new_password) {
return call(['bonafide', 'user', 'update', uid, current_password, new_password]);
}
}
},
/**
* For now the VPN setup is not really streamlined
*
* src/leap/bitmask/vpn/README.rst for more info
*/
vpn: {
enable: function() {
return call(['vpn', 'enable'])
},
disable: function() {
return call(['vpn', 'disable'])
},
status: function() {
return call(['vpn', 'status'])
},
start: function(provider) {
return call(['vpn', 'start', provider])
},
stop: function() {
return call(['vpn', 'stop'])
},
/**
* Check if the VPN is ready to start and has the cert downloaded
*
* @return {Promise<{'vpn_ready': bool,
* 'installed': bool}>}
*/
check: function(provider) {
if (typeof provider !== 'string') {
provider = "";
}
return call(['vpn', 'check', provider]);
},
/**
* Download VPN cert
*
* @param {string} userid the userid to be used
*/
get_cert: function(userid) {
return call(['vpn', 'get_cert', userid])
},
/**
* Install helpers in the system
*/
install: function() {
return call(['vpn', 'install'])
},
/**
* Uninstall helpers in the system
*/
uninstall: function() {
return call(['vpn', 'uninstall'])
}
/**
* List VPN gateways
*
* They will be sorted in the order that they will be used
*
* @return {Promise<{provider_name: [{'name': string,
* 'country_code': string,
* 'location': string,
* ...}]}>
*/
list: function() {
return call(['vpn', 'list'])
}
/**
* Get/set the location preference for the gateways
*
* @param {list} Order of preference of locations.
* If it's missing it will return the existing location list
*/
locations: function(locations) {
if (typeof locations !== 'list') {
locations = [];
}
return call(['vpn', 'locations'].concat(locations))
}
/**
* Get/set the country preference for the gateways
*
* @param {list} Order of preference of countries.
* If it's missing it will return the existing country list
*/
countries: function(countries) {
if (typeof countries !== 'list') {
countries = [];
}
return call(['vpn', 'countries'].concat(countries))
}
},
mail: {
enable: function() {
return call(['mail', 'enable'])
},
disable: function() {
return call(['mail', 'disable'])
},
/**
* Check the status of the email service
*
* @param {string} uid The uid to get status about
*
* @return {Promise} User readable status
*/
status: function(uid) {
return call(['mail', 'status', uid]);
},
/**
* Get the token of the active user.
*
* This token is used as password to authenticate in the IMAP and SMTP services.
*
* @param {string} uid The uid to get status about
*
* @return {Promise<{'token': string}>} The token
*/
get_token: function(uid) {
return call(['mail', 'get_token', uid]);
},
/**
* Get status on the mixnet for an address.
*
* @param {string} uid The uid to get status about
* @param {string} address The recipient address to be mixed
*
* @return {Promise<{'status': string}>} Where the status string can be 'ok',
* 'unsuported' or 'disabled'
*/
mixnet_status: function(uid, address) {
return call(['mail', 'mixnet_status', uid, address]);
}
},
/**
* A KeyObject have the following attributes:
* - address {string} the email address for wich this key is active
* - fingerprint {string} the fingerprint of the key
* - length {number} the size of the key bits
* - private {bool} if the key is private
* - uids {[string]} the uids in the key
* - key_data {string} the key content
* - validation {string} the validation level which this key was found
* - expiry_date {string} date when the key expires
* - refreshed_at {string} date of the last refresh of the key
* - audited_at {string} date of the last audit (unused for now)
* - sign_used {bool} if has being used to checking signatures
* - enc_used {bool} if has being used to encrypt
*/
keys: {
/**
* List all the keys in the keyring
*
* @param {string} uid The uid of the keyring.
* @param {boolean} priv Should list private keys?
* If it's not provided the public ones will be listed.
*
* @return {Promise<[KeyObject]>} List of keys in the keyring
*/
list: function(uid, priv) {
return call(['keys', 'list', uid, private_str(priv)]);
},
/**
* Export key
*
* @param {string} uid The uid of the keyring.
* @param {string} address The email address of the key
* @param {boolean} priv Should get the private key?
* If it's not provided the public one will be fetched.
* @param {boolean} fetch If the key is not in keymanager, should we fetch it remotely.
* If it's not provided keys will not be fetched remotely
*
* @return {Promise} The key
*/
exprt: function(uid, address, priv, fetch) {
var privstr = private_str(priv);
if ((typeof fetch === 'bool') && fetch) {
privstr = 'fetch';
}
return call(['keys', 'export', uid, address, privstr]);
},
/**
* Insert key
*
* @param {string} uid The uid of the keyring.
* @param {string} address The email address of the key
* @param {string} rawkey The key material
* @param {string} validation The validation level of the key
* If it's not provided 'Fingerprint' level will be used.
*
* @return {Promise} The key
*/
insert: function(uid, address, rawkey, validation) {
if (typeof validation !== 'string') {
validation = 'Fingerprint';
}
return call(['keys', 'insert', uid, address, validation, rawkey]);
},
/**
* Delete a key
*
* @param {string} uid The uid of the keyring.
* @param {string} address The email address of the key
* @param {boolean} priv Should get the private key?
* If it's not provided the public one will be deleted.
*
* @return {Promise} The key
*/
del: function(uid, address, priv) {
return call(['keys', 'delete', uid, address, private_str(priv)]);
}
},
events: {
/**
* Register func for an event
*
* @param {string} event The event to register
* @param {string} name The unique name for the callback
* @param {function} func The function that will be called on each event.
* It has to be like: function(event, content) {}
* Where content will be a list of strings.
*/
register: function(event, name, func) {
event_handlers[event] = event_handlers[event] || {}
if (event_handlers[event][name]) {
return null;
} else {
event_handlers[event][name] = func;
return call(['events', 'register', event])
}
},
/**
* Unregister from an event
*
* @param {string} event The event to unregister
* @param {string} name The unique name of the callback to remove
*/
unregister: function(event, name) {
event_handlers[event] = event_handlers[event] || {}
delete event_handlers[event][name]
if (Object.keys(event_handlers[event]).length == 0) {
return call(['events', 'unregister', event]);
} else {
return null;
}
}
}
};
}();
try {
module.exports = bitmask
} catch(err) {}