1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use tokio_core::reactor::Core;

use rincon_client::admin::methods::*;
use rincon_client::admin::types::{ServerVersion, TargetVersion};
use rincon_client::database::methods::{CreateDatabase, DropDatabase,
    ListDatabases, ListAccessibleDatabases};
use rincon_client::user::methods::*;
use rincon_client::user::types::{NewUser, Permission, User, UserExtra,
    UserUpdate};
use rincon_core::api::connector::{Connector, Execute};
use rincon_core::api::method::{Method, Prepare};
use rincon_core::api::types::Empty;
use rincon_core::arango::protocol::SYSTEM_DATABASE;

use database_session::DatabaseSession;
use super::Result;

/// A session for administrating databases and users.
///
/// An `ArangoSession` defines the entry point to the session api. It basically
/// determines which `Connector` implementation shall be used in an application
/// and provides functions for administrating databases and users.
#[derive(Debug)]
pub struct ArangoSession<C> {
    connector: Rc<C>,
    core: Rc<RefCell<Core>>,
}

impl<C> ArangoSession<C>
    where C: 'static + Connector
{
    /// Instantiates a new `ArangoSession` using the given `Connector`.
    pub fn new(connector: C, core: Core) -> Self {
        ArangoSession {
            connector: Rc::new(connector),
            core: Rc::new(RefCell::new(core)),
        }
    }

    /// Executes an API method applied to the system database.
    pub fn execute<M>(&self, method: M) -> Result<<M as Method>::Result>
        where M: 'static + Method + Prepare
    {
        self.core.borrow_mut().run(self.connector.system_connection().execute(method))
    }

    /// Gets the server name and version number.
    pub fn get_server_version(&self) -> Result<ServerVersion> {
        self.execute(GetServerVersion::new())
    }

    /// Gets the server name and version number with additional details.
    pub fn get_server_version_details(&self) -> Result<ServerVersion> {
        self.execute(GetServerVersion::with_details())
    }

    /// Gets the database version a server requires.
    pub fn get_target_version(&self) -> Result<TargetVersion> {
        self.execute(GetTargetVersion::new())
    }

    /// Returns a new `DatabaseSession` for the system database.
    ///
    /// In *ArangoDB* the system database usually has the name `_system`.
    pub fn use_system_database(&self) -> DatabaseSession<C> {
        DatabaseSession::new(SYSTEM_DATABASE.to_owned(), self.connector.clone(), self.core.clone())
    }

    /// Returns a new `DatabaseSession` for the given database name.
    pub fn use_database_with_name<N>(&self, database_name: N) -> DatabaseSession<C>
        where N: Into<String>
    {
        DatabaseSession::new(database_name.into(), self.connector.clone(), self.core.clone())
    }

    /// Creates a new database with the given attributes.
    ///
    /// If the database could be created successfully a `DatabaseSession` using
    /// the just created database is returned.
    ///
    /// The user provided with the `Connector` must have permission to access
    /// the system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `name`  : the name of the database to be created
    /// * `users` : a list of users to be assigned to the new database
    pub fn create_database<N, E>(&self, name: N, users: Vec<NewUser<E>>) -> Result<DatabaseSession<C>>
        where N: Into<String>, E: 'static + UserExtra + Serialize
    {
        let core = self.core.clone();
        let connector = self.connector.clone();
        let database_name = name.into();
        self.execute(CreateDatabase::with_name_for_users(database_name.clone(), users))
            .map(move |_| DatabaseSession::new(database_name, connector, core))
    }

    /// Drops an existing database with the given name.
    ///
    /// Returns true if the database has been dropped successfully.
    pub fn drop_database<N>(&self, name: N) -> Result<bool>
        where N: Into<String>
    {
        self.execute(DropDatabase::with_name(name))
    }

    /// Retrieves a list of all existing databases.
    ///
    /// The user set in the `Connector` must have permission to read from the
    /// system database in order to execute this method.
    pub fn list_databases(&self) -> Result<Vec<String>> {
        self.execute(ListDatabases::new())
    }

    /// Retrieves a list of all databases the current user has access to.
    pub fn list_accessible_databases(&self) -> Result<Vec<String>> {
        self.execute(ListAccessibleDatabases::new())
    }

    /// Creates a new user with default options.
    ///
    /// The created user will not have access to any database until database
    /// access privileges are explicitly granted to it.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    pub fn create_user<N, P, E>(&self, username: N, password: P) -> Result<User<E>>
        where N: Into<String>, P: Into<String>,
              E: 'static + UserExtra + Serialize + DeserializeOwned
    {
        self.execute(CreateUser::new(NewUser::with_name(username, password)))
    }

    /// Creates a new user with extra information.
    ///
    /// The created user will not have access to any database until database
    /// access privileges are explicitly granted to it.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    pub fn create_user_with_details<E>(&self, user: NewUser<E>) -> Result<User<E>>
        where E: 'static + UserExtra + Serialize + DeserializeOwned
    {
        self.execute(CreateUser::new(user))
    }

    /// Deletes an existing user with the given name.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    pub fn delete_user<N>(&self, username: N) -> Result<Empty>
        where N: Into<String>
    {
        self.execute(DeleteUser::with_name(username))
    }

    /// Fetches data about a user with the given name.
    ///
    /// This method can fetch data about the user set in the `Connector`. To
    /// retrieve data about any user the user set in the `Connector` must have
    /// permission to read from the system database.
    pub fn get_user<N, E>(&self, username: N) -> Result<User<E>>
        where N: Into<String>, E: 'static + UserExtra + Serialize + DeserializeOwned
    {
        self.execute(GetUser::with_name(username))
    }

    /// Fetches data about all available users.
    ///
    /// The user set in the `Connector` must have permission to read from the
    /// system database in order to execute this method.
    pub fn list_users<E>(&self) -> Result<Vec<User<E>>>
        where E: 'static + UserExtra + Serialize + DeserializeOwned
    {
        self.execute(ListUsers::new())
    }

    /// Partially updates the data of an existing user.
    ///
    /// The password can only be changed for the user set in the `Connector`.
    /// To change the active status the user set in the `Connector` must have
    /// permission to write to the system database.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which the data shall be replaced
    /// * `updates`  : the data to be updated for the given user
    pub fn modify_user<N, E>(&self, username: N, updates: UserUpdate<E>) -> Result<User<E>>
        where N: Into<String>, E: 'static + UserExtra + Serialize + DeserializeOwned
    {
        self.execute(ModifyUser::new(username.into(), updates))
    }

    /// Replaces the data of an existing user.
    ///
    /// The password can only be changed for the user set in the `Connector`.
    /// To change the active status the user set in the `Connector` must have
    /// permission to write to the system database.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which the data shall be replaced
    /// * `updates`  : the new data of the user
    pub fn replace_user<N, E>(&self, username: N, updates: UserUpdate<E>) -> Result<User<E>>
        where N: Into<String>, E: 'static + UserExtra + Serialize + DeserializeOwned
    {
        self.execute(ReplaceUser::new(username.into(), updates))
    }

    /// Lists all databases accessible by the given user.
    ///
    /// The user set in the `Connector` must have permission to read from the
    /// system database in order to execute this method.
    pub fn list_databases_for_user<N>(&self, username: N) -> Result<HashMap<String, Permission>>
        where N: Into<String>
    {
        self.execute(ListDatabasesForUser::new(username.into()))
    }

    /// Sets the default access level for databases for the given user.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which to grant default access
    /// * `permission` : the access level to grant
    pub fn grant_default_database_access<N>(&self, username: N, permission: Permission) -> Result<Empty>
        where N: Into<String>
    {
        self.execute(SetDatabaseAccessLevel::new(username.into(), "*".into(), permission))
    }

    /// Sets the default access level for collections for the given user.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which to grant default access
    /// * `permission` : the access level to grant
    pub fn grant_default_collection_access<N>(&self, username: N, permission: Permission) -> Result<Empty>
        where N: Into<String>
    {
        self.execute(SetCollectionAccessLevel::new(username.into(), "*".into(), "*".into(), permission))
    }

    /// Gets the effective access level to the specified database for the given
    /// user.
    ///
    /// The user set in the `Connector` must have permission to read from the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which the permissions are queried
    /// * `database` : the name of the database for which the permissions are queried
    pub fn get_database_access_level<N, Db>(&self, username: N, database: Db) -> Result<Permission>
        where N: Into<String>, Db: Into<String>
    {
        self.execute(GetDatabaseAccessLevel::new(username.into(), database.into()))
    }

    /// Grants access to the specified database for the given user.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which access shall be granted
    /// * `database` : the name of the database to which access shall be granted
    /// * `permission` : the access level that shall be granted
    pub fn grant_database_access<N, Db>(&self, username: N, database: Db, permission: Permission) -> Result<Empty>
        where N: Into<String>, Db: Into<String>
    {
        self.execute(SetDatabaseAccessLevel::new(username.into(), database.into(), permission))
    }

    /// Revokes the access to the specified database for the given user.
    ///
    /// After this call the given user has no access to the specified database.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which access shall be revoked
    /// * `database` : the name of the database from which access shall be revoked
    pub fn revoke_database_access<N, Db>(&self, username: N, database: Db) -> Result<Empty>
        where N: Into<String>, Db: Into<String>
    {
        self.execute(SetDatabaseAccessLevel::new(username.into(), database.into(), Permission::None))
    }

    /// Resets the access to the specified database for the given user to the
    /// default access level.
    ///
    /// After this call the default access level to the database is applied for
    /// the given user.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which access shall be reset to the default
    /// * `database` : the name of the database for which access shall be reset to the default
    pub fn reset_database_access<N, Db>(&self, username: N, database: Db) -> Result<Empty>
        where N: Into<String>, Db: Into<String>
    {
        self.execute(ResetDatabaseAccessLevel::new(username.into(), database.into()))
    }

    /// Gets the effective access level to the specified collection for the given
    /// user.
    ///
    /// The user set in the `Connector` must have permission to read from the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which the permissions are queried
    /// * `database` : the name of the database where the collection is located in
    /// * `collection` : the name of the collection for which the permissions are queried
    pub fn get_collection_access_level<N, Db, Coll>(&self, username: N, database: Db, collection: Coll) -> Result<Permission>
        where N: Into<String>, Db: Into<String>, Coll: Into<String>
    {
        self.execute(GetCollectionAccessLevel::new(username.into(), database.into(), collection.into()))
    }

    /// Grants access to the specified collection for the given user.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which access shall be granted
    /// * `database` : the name of the database where the collection is located
    /// * `collection` : the name of the collection to which access shall be granted
    /// * `permission` : the access level that shall be granted
    pub fn grant_collection_access<N, Db, Coll>(&self, username: N, database: Db, collection: Coll, permission: Permission) -> Result<Empty>
        where N: Into<String>, Db: Into<String>, Coll: Into<String>
    {
        self.execute(SetCollectionAccessLevel::new(username.into(), database.into(), collection.into(), permission))
    }

    /// Revokes the access to the specified collection for the given user.
    ///
    /// After this call the given user has no access to the specified collection.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which access shall be revoked
    /// * `database` : the name of the database where the collection is located
    /// * `collection` : the name of the collection from which access shall be revoked
    pub fn revoke_collection_access<N, Db, Coll>(&self, username: N, database: Db, collection: Coll) -> Result<Empty>
        where N: Into<String>, Db: Into<String>, Coll: Into<String>
    {
        self.execute(SetCollectionAccessLevel::new(username.into(), database.into(), collection.into(), Permission::None))
    }

    /// Resets the access to the specified collection for the given user to the
    /// default access level.
    ///
    /// After this call the default access level to the collection is applied
    /// for the given user.
    ///
    /// The user set in the `Connector` must have permission to write to the
    /// system database in order to execute this method.
    ///
    /// # Arguments
    ///
    /// * `username` : the name of the user for which access shall be reset to the default
    /// * `database` : the name of the database where the collection is located
    /// * `collection` : the name of the collection for which access shall be reset to the default
    pub fn reset_collection_access<N, Db, Coll>(&self, username: N, database: Db, collection: Coll) -> Result<Empty>
        where N: Into<String>, Db: Into<String>, Coll: Into<String>
    {
        self.execute(ResetCollectionAccessLevel::new(username.into(), database.into(), collection.into()))
    }
}