From 714deb60dcba67cf40a38b269568be980f120692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Fri, 17 Mar 2023 09:41:39 -0400 Subject: [PATCH] docs: update several pages + FAQ: clarify password per device + Remove some LRC pages (as included in client-qt now) + Updates some technical informations Change-Id: I55eedaff48ae6244bbfd09efeb2ce65a3a0996c2 --- developer/account-management.md | 142 ++++++++++--- developer/apis-of-jami.md | 243 +---------------------- developer/important-rfcs.md | 6 +- developer/libjamiclient-documentation.md | 81 -------- developer/technical-overview.md | 22 +- user/faq.rst | 4 + 6 files changed, 140 insertions(+), 358 deletions(-) delete mode 100644 developer/libjamiclient-documentation.md diff --git a/developer/account-management.md b/developer/account-management.md index f7f1c281..a2b54b7f 100644 --- a/developer/account-management.md +++ b/developer/account-management.md @@ -5,16 +5,6 @@ In this part, we will learn how to manage a Ring account. This means, how to cre ## Create a new account -### Client side - -#### Gnome - -The code related to this feature is located in `src/accountcreationwizard.*` - -#### LRC - -The account creation is mainly managed by the *NewAccountModel* in `src/api/newaccountmodel.h` and `src/newaccountmodel.cpp` - ### Daemon side #### API @@ -82,7 +72,7 @@ A Ring account is in fact represented by some files stored in a gzip archive. If 1. The private key `ringAccountKey` and certificate chain `ringAccountCert` (base64 encoded) 2. Generated CA key (for self-signed certificates) `ringCAKey` 3. Revocated devices `ringAccountCRL` -4. The ethereum private key `ethKey` for the device. It's only used when you register your name on `ns.ring.cx`. Not mandatory. +4. The ethereum private key `ethKey` for the device. It's only used when you register your name on `ns.jami.net`. Not mandatory. 5. The contacts 6. The account settings @@ -91,20 +81,10 @@ So let's generate it! **TODO** - ## Delete the account -Deleting a Ring account is pretty simple. Because the keys are only on the device, if the keys are deleted... the account is deleted! The only thing outside the device is the username, on the nameserver. To remove this info, it depends how the nameserver work. For example, it's not possible with https://ns.ring.cx - -### Client side - -#### Gnome - -The code related to this feature is located in `newaccountsettingsview.cpp` - -#### LRC +Deleting a Ring account is pretty simple. Because the keys are only on the device, if the keys are deleted... the account is deleted! The only thing outside the device is the username, on the nameserver. To remove this info, it depends how the nameserver work. For example, it's not possible with https://ns.jami.net -The account deletion is in the *NewAccountModel* in `src/api/newaccountmodel.h` and `src/newaccountmodel.cpp` (`removeAccount`) ### Daemon side @@ -131,10 +111,120 @@ When the account is deleted, the signal `accountsChanged` will be emitted. The c The main logic to create a new account is located in `src/manager.cpp`, in `Manager::removeAccount`. It removes the accounts files and update the config (`dring.yml`). -## Update the settings +## Update the details of an account -**TODO** +### API -## Add and revoke devices +In cx.ring.Ring.ConfigurationManager: -**TODO** +```xml +<method name="setAccountDetails" tp:name-for-bindings="setAccountDetails"> + <tp:docstring> + Send new account parameters, or account parameters changes, to the core. The hash table is not required to be complete, only the updated parameters may be specified. + <tp:rationale>Account settings are written to the configuration file when ring properly quits.</tp:rationale> + <tp:rationale>After calling this method, the core will emit the signal <tp:member-ref>accountDetailsChanged</tp:member-ref> with the updated data. The client must subscribe to this signal and use it to update its internal data structure.</tp:rationale> + </tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="MapStringString"/> + <arg type="a{ss}" name="details" direction="in" tp:type="String_String_Map"> + </arg> +</method> +``` + +The map can contains a partial update and `accountDetailsChanged` will be emitted on success. `getAccountDetails` + +## Add a device + +There is two possibilities to add a device. + +### Backup archive (Then import from backup) + +#### API + +In cx.ring.Ring.ConfigurationManager: +```xml +<method name="exportToFile" tp:name-for-bindings="exportToFile"> + <tp:added version="5.1.0"/> + <tp:docstring> + Copy the account archive to the path provided in argument. + </tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <arg type="s" name="destinationPath" direction="in"> + </arg> + <arg type="s" name="password" direction="in"> + </arg> + <arg type="b" name="success" direction="out"> + <tp:docstring> + True if the operation was initialized successfully. + </tp:docstring> + </arg> +</method> +``` + +### Export on DHT + +#### API + +In cx.ring.Ring.ConfigurationManager: +```xml +<method name="exportOnRing" tp:name-for-bindings="exportOnRing"> + <tp:docstring> + Export account on the DHT using the given password and generated PIN (returned through exportOnRingEnded signal). + </tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <arg type="s" name="password" direction="in"> + </arg> + <arg type="b" name="success" direction="out"> + <tp:docstring> + True if the operation was initialized successfully. exportOnRingEnded will be trigered on completion. + </tp:docstring> + </arg> +</method> +``` + +Then `exportOnRingEnded` is emitted. + +## Revoke device + +### API + +```xml +<method name="revokeDevice" tp:name-for-bindings="revokeDevice"> + <tp:docstring> + Revoke device attached to the given Ring account, and publish the new revocation list. + </tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <arg type="s" name="password" direction="in"> + </arg> + <arg type="s" name="deviceId" direction="in"> + </arg> + <arg type="b" name="success" direction="out"> + <tp:docstring> + True if the operation was performed successfully. + </tp:docstring> + </arg> +</method> +<signal name="deviceRevocationEnded" tp:name-for-bindings="deviceRevocationEnded"> + <tp:docstring> + Notify clients when the revokeDevice operation ended. + </tp:docstring> + <arg type="s" name="accountID"> + </arg> + <arg type="s" name="deviceId"> + </arg> + <arg type="i" name="status"> + <tp:docstring> + Status code: 0 for success + <ul> + <li>SUCCESS = 0 everything went fine. Device is now revoked.</li> + <li>WRONG_PASSWORD = 1 revocation failed: wrong password.</li> + <li>UNKNOWN_DEVICE = 2 revocation failed: unknown device.</li> + </ul> + </tp:docstring> + </arg> +</signal> +``` \ No newline at end of file diff --git a/developer/apis-of-jami.md b/developer/apis-of-jami.md index d9f40f99..0ac61c3f 100644 --- a/developer/apis-of-jami.md +++ b/developer/apis-of-jami.md @@ -28,23 +28,6 @@ The LRC project uses this API (and use libwrap on windows and mac os). All the documentation and code for the JNI API is located in `jami-daemon/bin/jni`. -#### Generation process - -- `cd jami-project/` -- Check `./daemon/src/jamid` -- Create a new file .i in `./daemon/bin/jni/` from an existing file in the same folder -- Update with your new interfaces from documentation -- Check that callback block is at the beginning AND at the end of file -- `cd daemon/bin/jni` -- Edit ./daemon/bin/jni/jni\_interface.i and add yours with the other .i files that are in the same folder -- `export PACKAGEDIR=\*\*\*\*/jami-project/client-android/ring-android/libjamiclient/src/main/kotlin/net/jami.` - ie: `export PACKAGEDIR=/home/pduchemin/Documents/jami-project/client-android/ring-android/libjamiclient/src/main/kotlin/net/jami` -- Run - - `./make-swig.sh` -- or, if you start from scratch - - `./build.py --init --distribution=Android` - - `./build.py --install --distribution=Android` - ### node js All the documentation and code for the Node JS API is located in `jami-daemon/bin/nodejs`. This API is not used in any known project and maybe is not up-to-date. @@ -55,228 +38,4 @@ All the documentation and code for the REST API is located in `jami-daemon/bin/r ### Python wrapper -A Python wrapper is available in `jami-daemon/tools/jamictrl`. This wrapper uses DBus. - -This is, for example a quick and dirty IRC bot to interact with Jami made with this API: - -`config.json`: -```json -{ - "users": { - }, - "debug": true, - "host": "irc.freenode.net", - "follow_all": { - }, - "port": 6697, - "nick": "NICKNAME", - "ssl": true, - "channel": "#REPLACE" -} -``` - -`irc,py`: -```python -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -from controler import DRingCtrl -from threading import Thread -import irc3 -from irc3 import utils -import json -import sys - - -@irc3.plugin -class RingBridge: - '''This plug-in react to event from IRC and send messages to IRC''' - - requires = [ - 'irc3.plugins.core', - 'irc3.plugins.userlist', - 'irc3.plugins.command', - 'irc3.plugins.human', - ] - - def __init__(self, bot): - self.bot = bot - self.log = self.bot.log - self.channels = utils.as_list(self.bot.config.get('autojoins', [])) - self.config = None - - @irc3.event(irc3.rfc.PRIVMSG) - def on_privmsg(self, mask=None, target=None, data=None, **kwargs): - '''React to messages from IRC''' - message = data.split(' ') - follow_all = self.bot.config_bridge['follow_all'] - if message[0] == '!tell': - msg_to_send = ('%s: ' % mask.nick) + ''.join( - '%s ' % m for m in message[2:])[:-1] - call_id = None - users = self.bot.config_bridge['users'] - # Get ring_id from nick - for ring_id in users.keys(): - if users[ring_id].lower() == message[1].lower(): - call_id = ring_id - if call_id: - # if this nick want to receive all messages, we don't need - # to send the message here - if call_id not in follow_all.keys() \ - or not follow_all[call_id] \ - or target == self.bot.config_bridge['channel']: - print('Send: %s to %s' % (msg_to_send, call_id)) - self.bot.controler.sendTextMessage(call_id, - msg_to_send) - else: - self.bot.privmsg(self.bot.config_bridge['channel'], - 'I don\'t know how to contact this person') - # Send message to everyone who wants to receive message from #channel - if target.lower() != self.bot.config_bridge['channel'].lower(): - return - msg_to_send = ('%s: %s' % (mask.nick, data)) - for user in follow_all.keys(): - if follow_all[user]: - self.bot.controler.sendTextMessage(user, - msg_to_send) - - @irc3.extend - def send_message_to_irc(self, ring_id, message): - '''send message to #channel''' - self.bot.privmsg(self.bot.config_bridge['channel'], - '%s: %s' % - (self.bot.config_bridge['users'][ring_id], message), - True) - - -def threadClient(controler, config_bridge): - config = dict( - nick=config_bridge['nick'], - autojoins=[config_bridge['channel']], - host=config_bridge['host'], - port=config_bridge['port'], - ssl=config_bridge['ssl'], - debug=config_bridge['debug'], - includes=[__name__] - ) - bot = irc3.IrcBot.from_config(config) - # link irc and ring controlers - bot.controler = controler - bot.config_bridge = config_bridge - controler.irc_client = bot - - bot.run(forever=True) - - -class IRCRingController(DRingCtrl): - def __init__(self, config_bridge): - super().__init__('ircbridge', True) - self.irc_client = None - self.config_bridge = config_bridge - self.accountId = self.configurationmanager.getAccountList()[0] - - def updateConfig(self): - '''Change actual config''' - with open('config.json', 'w') as config_file: - json.dump(self.config_bridge, config_file, indent=4) - if self.irc_client: - self.irc_client.config_bridge = self.config_bridge - - def onIncomingAccountMessage(self, accountId, fromAccount, payloads): - '''React to message from Ring''' - # Avoid to react to message from self. - if fromAccount == self.accountId: - return - # If we have multiple accounts, avoid to react from another account - if accountId != self.accountId: - return - message = '%s: %s' % (fromAccount, payloads['text/plain']) - print('Receive new message from %s' % message) - # React to !commands - if payloads['text/plain'] == '!follow': - self.config_bridge['follow_all'][fromAccount] = True - self.updateConfig() - elif payloads['text/plain'] == '!unfollow': - self.config_bridge['follow_all'][fromAccount] = False - self.updateConfig() - elif payloads['text/plain'].split(' ')[0] == '!add': - irc_nick = payloads['text/plain'].split(' ')[1] - self.config_bridge['users'][fromAccount] = irc_nick - self.updateConfig() - elif payloads['text/plain'] == '!rm': - del self.config_bridge['users'][fromAccount] - del self.config_bridge['follow_all'][fromAccount] - self.updateConfig() - # Send message to IRC - else: - try: - if self.irc_client: - self.irc_client.send_message_to_irc(fromAccount, - payloads['text/plain']) - except: - print('Can\'t read message received: %s' % payloads) - - def sendTextMessage(self, accountId, message): - '''Send a message to a ring id''' - if accountId == self.accountId: - return - self.configurationmanager.sendTextMessage(self.accountId, - accountId, - { - 'text/plain': - str(message) - }) - - -if __name__ == '__main__': - config_bridge = None - with open('config.json') as config_file: - config_bridge = json.loads(config_file.read()) - if not config_bridge: - print('Can\'t find config.json') - sys.exit(-1) - irc_controler = IRCRingController(config_bridge) - thread = Thread(target=threadClient, args=(irc_controler, config_bridge,)) - thread.start() - irc_controler.run() -``` - -## LRC - -### Doxygen doc - -The Doxygen documentation is available [here](https://jenkins.ring.cx/view/libringclient/job/libringclient-doxygen/doxygen/annotated.html) and currently generated by Jenkins each week. - -### Database schema - - - -```sql -CREATE TABLE profiles (id INTEGER PRIMARY KEY, \ - uri TEXT NOT NULL, \ - alias TEXT, \ - photo TEXT, \ - type TEXT, \ - status TEXT); - -CREATE TABLE conversations (id INTEGER,\ - participant_id INTEGER, \ - FOREIGN KEY(participant_id) REFERENCES profiles(id)); - -CREATE TABLE interactions (id INTEGER PRIMARY KEY,\ - account_id INTEGER, \ - author_id INTEGER, \ - conversation_id INTEGER, \ - timestamp INTEGER, \ - body TEXT, \ - type TEXT, \ - status TEXT, \ - daemon_id TEXT, \ - FOREIGN KEY(account_id) REFERENCES profiles(id), \ - FOREIGN KEY(author_id) REFERENCES profiles(id), \ - FOREIGN KEY(conversation_id) REFERENCES conversations(id)); - -CREATE TABLE profiles_accounts (profile_id INTEGER NOT NULL, \ - account_id TEXT NOT NULL, \ - is_account TEXT, \ - FOREIGN KEY(profile_id) REFERENCES profiles(id)); -``` +A Python wrapper is available in `jami-daemon/tools/jamictrl`. This wrapper uses DBus. \ No newline at end of file diff --git a/developer/important-rfcs.md b/developer/important-rfcs.md index 359ab1eb..01811c0d 100644 --- a/developer/important-rfcs.md +++ b/developer/important-rfcs.md @@ -13,6 +13,10 @@ Valuable updates and extensions: Reference: <http://tools.ietf.org/html/rfc5245> +## ICE TCP + +Reference: <http://tools.ietf.org/html/rfc6544> + ## SDP Reference: <http://tools.ietf.org/html/rfc4566> @@ -23,7 +27,7 @@ Valuable updates and extensions: + SDP and IPv6: <http://tools.ietf.org/html/rfc6157> + SDP for SRTP: <http://tools.ietf.org/html/rfc4568> - + ## RTP Reference: <http://tools.ietf.org/html/rfc3550> diff --git a/developer/libjamiclient-documentation.md b/developer/libjamiclient-documentation.md deleted file mode 100644 index cf81476c..00000000 --- a/developer/libjamiclient-documentation.md +++ /dev/null @@ -1,81 +0,0 @@ -libjamiclient documentation -=========================== - -*This page is a stub.* - -### Introduction - -- Lrc (Libringclient) is an interface between the clients and - the daemon. It ensures to get the same behavior for all client - using it. - - -note: red = missing feature. - -### DatabaseManager - -- this class is an interface between lrc and the sqlite database. This - class should not be called directly from the client. - - - -### NewCallModel class and NewCall namespace - -- NewCallModel is an interface used to manage the calls. - -<!-- --> - -- When we need information about calls, members functions from the - model can return NewCall::Info. - -<!-- --> - -- When we need to perform some operation on a call, we pass it callId - to the delegate performing the operation. - -<!-- --> - -- note about the name : we are using New as prefix to avoid conflict - with the current CallModel and Call objects. - - - -### ContactModel class and Contact namespace - -- ContactModel is an interface to manage the contacts. - -<!-- --> - -- When we need information about contact(s), members functions from - the model can return Contact::Info - -<!-- --> - -- When we need to perform some operation on a contact, we pass it uri - to the delegate performing the operation. - - - -### ConversationModel class, Conversation namespace and Message namespace - -- ConversationModel is an interface used to manage conversations - and messages. - -<!-- --> - -- When we need information about some conversation(s), members - functions from the model can return Conversation::Info. - Conversation::Info contains Messages. - -<!-- --> - -- note about Message : so far, we didn't need a MessageModel, but this - could be come soon. - - - -### resources - -[Diagram1.dia](https://git.jami.net/savoirfairelinux/jami-project/uploads/f6d0a81d67b075c13c57b7d9771ca065/Diagram1.dia) - -[Diagram2.dia](https://git.jami.net/savoirfairelinux/jami-project/uploads/00b3212a7642e58fd35b74c48e712332/Diagram2.dia) diff --git a/developer/technical-overview.md b/developer/technical-overview.md index a52ac03d..a28396ac 100644 --- a/developer/technical-overview.md +++ b/developer/technical-overview.md @@ -114,7 +114,7 @@ Concepts ``` salt = PIN + timestamp key = argon2(password, salt) - argon2 is the argon2i algorithm using t_cost = 16, m_cost = 2^16 (64 MiB), mono-threaded, to generate a 512-bits key and then hashed with SHA-256 to generate a 256-bits key. + argon2 is the argon2i algorithm using t_cost = 16, m_cost = 2^16 (64 MiB), mono-threaded, to generate a 512-bits key and then hashed with SHA-256 to generate a 256-bits key. PIN is a random 32bits number in hexadecimal form. + is string concatenation operator. timestamp is the current UNIX timestamp divided by 1200 (20 minutes). @@ -137,10 +137,6 @@ key = argon2(password, salt) - PEM format - not encrypted, we let the device file-system protect this file -#### Exporting data (creating new devices) - -*TBD* - ### The DHT network Dedicated [ Jami distributed @@ -148,11 +144,20 @@ network](Ring_distributed_network "wikilink") page. ### Contact Request -*TBD* +- Deprecated in favor of "Conversation requests" + +### Conversation request + +- Max 64k (a DHT value) +- Contains a conversation Id +- Contains the sender URI +- Can contains optional metadatas (avatar/profile) ### Instant Message -*TBD* +- Mostly used to initiate connections with ICE candidates +- Can transmit some SIP messages, however SIP channel is preferred +- SIP messages can be read status, location sharing, messages notifications. ### Outgoing and Incoming calls @@ -278,7 +283,8 @@ network](Ring_distributed_network "wikilink") page. ### Presence -*TBD* +- Sent on the DHT +- DeviceAnnouncement (contains device hash + public key) ### Security / Privacy diff --git a/user/faq.rst b/user/faq.rst index afbbd86c..674337e5 100644 --- a/user/faq.rst +++ b/user/faq.rst @@ -269,6 +269,10 @@ If your device is encrypted, you may not want or need to use a password, and indeed recent versions of Jami don't ask for an account encryption password by default when creating new accounts. +Note: changing a password will only change the password on the current +device and it's not synced (because their is no server and other devices +can be offline anyway). + Why don't I have to register a username? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- GitLab