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
296
    pub(super) fn begin_token_authentication(
18
296
        &self,
19
296
        id: u64,
20
296
        request_time: TimestampAsNanoseconds,
21
296
        request_time_check: &[u8],
22
296
        algorithm: TokenChallengeAlgorithm,
23
296
        admin: &Database,
24
296
    ) -> Result<Storage, bonsaidb_core::Error> {
25
296
        // TODO alow configuration of timestamp sensitivity. 5 minutes chosen based on Kerberos and
26
296
        if request_time
27
296
            .duration_between(&TimestampAsNanoseconds::now())?
28
296
            .as_secs()
29
            > 300
30
        {
31
            return Err(bonsaidb_core::Error::InvalidCredentials); // TODO better error
32
296
        }
33
296
        let token = AuthenticationToken::get(&id, admin)?
34
296
            .ok_or(bonsaidb_core::Error::InvalidCredentials)?;
35
296
        AuthenticationToken::check_request_time(
36
296
            request_time,
37
296
            request_time_check,
38
296
            algorithm,
39
296
            &token.contents.token,
40
296
        )?;
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
296
        let mut sessions = self.data.sessions.write();
47
296
        sessions.last_session_id += 1;
48
296
        let session_id = SessionId(sessions.last_session_id);
49
296
        let nonce = thread_rng().gen::<[u8; 32]>();
50
296
        let session = Session {
51
296
            id: Some(session_id),
52
296
            authentication: SessionAuthentication::TokenChallenge {
53
296
                id,
54
296
                algorithm: TokenChallengeAlgorithm::Blake3,
55
296
                nonce,
56
296
                server_timestamp: TimestampAsNanoseconds::now(),
57
296
            },
58
296
            permissions: Permissions::default(), /* This session will have no permissions until it finishes token authentication */
59
296
        };
60
296
        let authentication = Arc::new(AuthenticatedSession {
61
296
            storage: Arc::downgrade(&self.data),
62
296
            session: Mutex::new(session.clone()),
63
296
        });
64
296
        sessions.sessions.insert(session_id, authentication.clone());
65
296

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

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