1
1
//! Shows basic usage of users and permissions.
2

            
3
use std::time::Duration;
4

            
5
use bonsaidb::{
6
    client::{url::Url, Client},
7
    core::{
8
        admin::PermissionGroup,
9
        connection::{Authentication, SensitiveString, StorageConnection},
10
        permissions::{
11
            bonsai::{AuthenticationMethod, BonsaiAction, ServerAction},
12
            Permissions, Statement,
13
        },
14
        schema::{InsertError, SerializedCollection},
15
    },
16
    local::config::Builder,
17
    server::{Server, ServerConfiguration},
18
};
19

            
20
mod support;
21
use support::schema::Shape;
22

            
23
#[tokio::main]
24
2
async fn main() -> anyhow::Result<()> {
25
2
    drop(env_logger::try_init());
26
45
    let server = setup_server().await?;
27

            
28
    // Create a database user, or get its ID if it already existed.
29
5
    let user_id = match dbg!(server.create_user("ecton").await) {
30
1
        Ok(id) => {
31
1
            // Set the user's password.
32
1
            server
33
4
                .set_user_password("ecton", SensitiveString("hunter2".to_string()))
34
4
                .await?;
35

            
36
1
            id
37
        }
38
        Err(bonsaidb::core::Error::UniqueKeyViolation {
39
1
            existing_document, ..
40
1
        }) => existing_document.id.deserialize()?,
41
        Err(other) => anyhow::bail!(other),
42
    };
43

            
44
    // Create an administrators permission group, or get its ID if it already existed.
45
2
    let admin = server.admin().await;
46
2
    let administrator_group_id = match (PermissionGroup {
47
2
        name: String::from("administrators"),
48
2
        statements: vec![Statement::allow_all_for_any_resource()],
49
2
    }
50
5
    .push_into(&admin)
51
5
    .await)
52
    {
53
1
        Ok(doc) => doc.header.id,
54
        Err(InsertError {
55
            error:
56
                bonsaidb::core::Error::UniqueKeyViolation {
57
1
                    existing_document, ..
58
1
                },
59
1
            ..
60
1
        }) => existing_document.id.deserialize()?,
61
        Err(other) => anyhow::bail!(other),
62
    };
63

            
64
    // Make our user a member of the administrators group.
65
2
    server
66
3
        .add_permission_group_to_user(user_id, administrator_group_id)
67
3
        .await?;
68

            
69
    // ANCHOR_END: setup
70

            
71
    // Give a moment for the listeners to start.
72
2
    tokio::time::sleep(Duration::from_millis(10)).await;
73

            
74
2
    let client = Client::build(Url::parse("bonsaidb://localhost")?)
75
        .with_certificate(
76
2
            server
77
2
                .certificate_chain()
78
                .await?
79
2
                .into_end_entity_certificate(),
80
2
        )
81
2
        .finish()
82
        .await?;
83
2
    let db = client.database::<Shape>("my-database").await?;
84

            
85
    // Before authenticating, inserting a shape shouldn't work.
86
2
    match Shape::new(3).push_into(&db).await {
87
        Err(InsertError {
88
2
            error: bonsaidb::core::Error::PermissionDenied(denied),
89
2
            ..
90
2
        }) => {
91
2
            log::info!(
92
                "Permission was correctly denied before logging in: {:?}",
93
                denied
94
            );
95
        }
96
        _ => unreachable!("permission shouldn't be allowed"),
97
    }
98

            
99
    // Now, log in and try again.
100
2
    client
101
2
        .authenticate(
102
2
            "ecton",
103
2
            Authentication::Password(SensitiveString(String::from("hunter2"))),
104
2
        )
105
2
        .await
106
2
        .unwrap();
107
2
    let shape_doc = Shape::new(3).push_into(&db).await?;
108
2
    log::info!("Successully inserted document {:?}", shape_doc);
109

            
110
2
    drop(db);
111
2
    drop(client);
112
2

            
113
2
    // Shut the server down gracefully (or forcefully after 5 seconds).
114
2
    server.shutdown(Some(Duration::from_secs(5))).await?;
115

            
116
2
    Ok(())
117
2
}
118

            
119
2
async fn setup_server() -> anyhow::Result<Server> {
120
2
    let server = Server::open(
121
2
        ServerConfiguration::new("users-server-data.bonsaidb")
122
2
            .default_permissions(Permissions::from(
123
2
                Statement::for_any()
124
2
                    .allowing(&BonsaiAction::Server(ServerAction::Connect))
125
2
                    .allowing(&BonsaiAction::Server(ServerAction::Authenticate(
126
2
                        AuthenticationMethod::PasswordHash,
127
2
                    ))),
128
2
            ))
129
2
            .with_schema::<Shape>()?,
130
33
    )
131
33
    .await?;
132
4
    if server.certificate_chain().await.is_err() {
133
6
        server.install_self_signed_certificate(true).await?;
134
1
    }
135
2
    server.create_database::<Shape>("my-database", true).await?;
136

            
137
    // Spawn our QUIC-based protocol listener.
138
2
    let task_server = server.clone();
139
11
    tokio::spawn(async move { task_server.listen_on(5645).await });
140
2

            
141
2
    Ok(server)
142
2
}
143

            
144
1
#[test]
145
1
fn runs() {
146
1
    main().unwrap();
147
1
    main().unwrap();
148
1
}