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
use super::User;
use std::{collections::HashMap, env, sync::Mutex};

const HADOOP_PROXY_USER: &str = "HADOOP_PROXY_USER";

/// Information about the logged in user.
static LOGIN_USER_REF: Mutex<Option<UserGroupInformation>> = Mutex::new(None);

/// User and group information for Hadoop.
/// This class provides methods to determine the
/// user's username and groups. It supports both the Windows, Unix and Kerberos
/// login modules.
#[derive(Clone, Copy)]
pub struct UserGroupInformation {
    user: User,
}

impl UserGroupInformation {
    /// A method to initialize the fields that depend on a configuration.
    /// Must be called before useKerberos or groups is used.
    fn ensure_initialized() {
        // TODO
    }

    /// Return the current user, including any doAs in the current stack.
    pub fn get_current_user() -> anyhow::Result<Self> {
        Self::ensure_initialized();
        // TODO: use context user if any
        Self::get_login_user()
    }

    /// Get the currently logged in user.  If no explicit login has occurred,
    /// the user will automatically be logged in with either kerberos credentials
    /// if available, or as the local OS user, based on security settings.
    pub fn get_login_user() -> anyhow::Result<Self> {
        Self::ensure_initialized();
        // TODO: confirm whether to use Atomic or Mutex for LOGIN_USER_REF
        if let Some(login_user) = *LOGIN_USER_REF.lock().unwrap() {
            return Ok(login_user);
        }
        let new_login_user = Self::create_login_user(None)?;
        let login_user = *LOGIN_USER_REF.lock().unwrap().get_or_insert_with(|| {
            new_login_user.spawn_auto_renewal_thread_for_user_creds(false);
            new_login_user
        });
        Ok(login_user)
    }

    fn create_login_user(subject: Option<&str>) -> anyhow::Result<Self> {
        let real_user = Self::do_subject_login(subject, None)?;

        // If the HADOOP_PROXY_USER environment variable
        // is specified, create a proxy user as the logged in user.
        let proxy_user = env::var(HADOOP_PROXY_USER).ok().filter(|p| !p.is_empty());
        let login_user = proxy_user.map_or(real_user, |p| Self::create_proxy_user(&p, &real_user));

        // TODO: load tokens from files and base64 encoding

        Ok(login_user)
    }

    /// Spawn a thread to do periodic renewals of kerberos credentials. NEVER
    /// directly call this method. This method should only be used for ticket cache
    /// based kerberos credentials.
    fn spawn_auto_renewal_thread_for_user_creds(&self, _force: bool) {
        // TODO
    }

    /// Create a proxy user using username of the effective user and the ugi of the
    /// real user.
    pub fn create_proxy_user(_user: &str, real_user: &Self) -> Self {
        // TODO
        real_user.to_owned()
    }

    /// Get the user's login name.
    pub fn get_short_user_name(&self) -> String {
        self.user.get_short_name()
    }

    /// Get the user's full principal name.
    pub fn get_user_name(&self) -> String {
        self.user.get_name()
    }

    /// Login a subject with the given parameters.  If the subject is null,
    /// the login context used to create the subject will be attached.
    fn do_subject_login(
        _subject: Option<&str>,
        _params: Option<HashMap<String, String>>,
    ) -> anyhow::Result<Self> {
        // TODO
        Ok(Self { user: User {} })
    }
}