1
use std::sync::Arc;
2

            
3
use bonsaidb_core::admin::{AuthenticationToken, Role, User};
4
use bonsaidb_core::connection::{
5
    IdentityId, Session, SessionAuthentication, SessionId, TokenChallengeAlgorithm,
6
};
7
use bonsaidb_core::key::time::TimestampAsNanoseconds;
8
use bonsaidb_core::permissions::Permissions;
9
use bonsaidb_core::schema::SerializedCollection;
10
use parking_lot::Mutex;
11
use rand::{thread_rng, Rng};
12

            
13
use crate::storage::AuthenticatedSession;
14
use crate::{Database, Storage};
15

            
16
impl super::StorageInstance {
17
304
    pub(super) fn begin_token_authentication(
18
304
        &self,
19
304
        id: u64,
20
304
        request_time: TimestampAsNanoseconds,
21
304
        request_time_check: &[u8],
22
304
        algorithm: TokenChallengeAlgorithm,
23
304
        admin: &Database,
24
304
    ) -> Result<Storage, bonsaidb_core::Error> {
25
304
        // TODO alow configuration of timestamp sensitivity. 5 minutes chosen based on Kerberos and
26
304
        if request_time
27
304
            .duration_between(&TimestampAsNanoseconds::now())?
28
304
            .as_secs()
29
            > 300
30
        {
31
            return Err(bonsaidb_core::Error::InvalidCredentials); // TODO better error
32
304
        }
33
304
        let token = AuthenticationToken::get(&id, admin)?
34
304
            .ok_or(bonsaidb_core::Error::InvalidCredentials)?;
35
304
        AuthenticationToken::check_request_time(
36
304
            request_time,
37
304
            request_time_check,
38
304
            algorithm,
39
304
            &token.contents.token,
40
304
        )?;
41

            
42
        // Token authentication creates a temporary session for the token
43
        // challenge. The process of finishing token authentication will remove
44
        // the session.
45

            
46
304
        let mut sessions = self.data.sessions.write();
47
304
        sessions.last_session_id += 1;
48
304
        let session_id = SessionId(sessions.last_session_id);
49
304
        let nonce = thread_rng().gen::<[u8; 32]>();
50
304
        let session = Session {
51
304
            id: Some(session_id),
52
304
            authentication: SessionAuthentication::TokenChallenge {
53
304
                id,
54
304
                algorithm: TokenChallengeAlgorithm::Blake3,
55
304
                nonce,
56
304
                server_timestamp: TimestampAsNanoseconds::now(),
57
304
            },
58
304
            permissions: Permissions::default(), /* This session will have no permissions until it finishes token authentication */
59
304
        };
60
304
        let authentication = Arc::new(AuthenticatedSession {
61
304
            storage: Arc::downgrade(&self.data),
62
304
            session: Mutex::new(session.clone()),
63
304
        });
64
304
        sessions.sessions.insert(session_id, authentication.clone());
65
304

            
66
304
        Ok(Storage {
67
304
            instance: self.clone(),
68
304
            authentication: Some(authentication),
69
304
            effective_session: Some(Arc::new(session)),
70
304
        })
71
304
    }
72

            
73
304
    pub(super) fn finish_token_authentication(
74
304
        &self,
75
304
        session_id: SessionId,
76
304
        hash: &[u8],
77
304
        admin: &Database,
78
304
    ) -> Result<Storage, bonsaidb_core::Error> {
79
        // Remove the temporary session so that it can't be used multiple times.
80
304
        let session = {
81
304
            let mut sessions = self.data.sessions.write();
82
304
            sessions
83
304
                .sessions
84
304
                .remove(&session_id)
85
304
                .ok_or(bonsaidb_core::Error::InvalidCredentials)?
86
        };
87
304
        let session = session.session.lock();
88
304
        match &session.authentication {
89
            SessionAuthentication::TokenChallenge {
90
304
                id,
91
304
                algorithm,
92
304
                nonce,
93
304
                server_timestamp,
94
            } => {
95
304
                let token = AuthenticationToken::get(id, admin)?
96
304
                    .ok_or(bonsaidb_core::Error::InvalidCredentials)?;
97
304
                token
98
304
                    .contents
99
304
                    .validate_challenge(*algorithm, *server_timestamp, nonce, hash)?;
100
304
                match token.contents.identity {
101
152
                    IdentityId::User(id) => {
102
152
                        let user = User::get(&id, admin)?
103
152
                            .ok_or(bonsaidb_core::Error::InvalidCredentials)?;
104
152
                        self.assume_user(user, admin)
105
                    }
106
152
                    IdentityId::Role(id) => {
107
152
                        let role = Role::get(&id, admin)?
108
152
                            .ok_or(bonsaidb_core::Error::InvalidCredentials)?;
109
152
                        self.assume_role(role, admin)
110
                    }
111
                    _ => Err(bonsaidb_core::Error::InvalidCredentials),
112
                }
113
            }
114
            SessionAuthentication::None | SessionAuthentication::Identity(_) => {
115
                Err(bonsaidb_core::Error::InvalidCredentials)
116
            }
117
        }
118
304
    }
119
}