Skip to content
Snippets Groups Projects
nss.rs 5.51 KiB
Newer Older
/* Copyright 2021 Dominik George <dominik.george@teckids.org>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::config::{
    get_config,
    get_optional,
    get_or_error
use config::Config;

use crate::logging::setup_log;

use oauth2::{
    AuthUrl,
    ClientId,
    ClientSecret,
    RequestTokenError,
    Scope,
    TokenUrl
};
use oauth2::basic::BasicClient;
use oauth2::reqwest;
use oauth2::reqwest::http_client;

use libnss::interop::Response;
use libnss::passwd::{PasswdHooks, Passwd};

fn nss_hook_prepare() -> Config {
    let conf = get_config(None);

    let mut log_level = log::LevelFilter::Error;
    if conf.get_bool("debug").unwrap_or_default() || conf.get_bool("nss.debug").unwrap_or_default() {
        log_level = log::LevelFilter::Debug;
    }
    setup_log(log_level);

    return conf;
}

fn get_bearer_token(config: Config) -> Result<String, Response> {
    let client_id = ClientId::new(get_or_error(&config, "nss.client_id", Response::Unavail)?);
    let client_secret = match get_optional(&config, "nss.client_secret") {
        Some(v) => Some(ClientSecret::new(v)),
        None => None,
    };
    let auth_url = match AuthUrl::new(get_or_error(&config, "nss.auth_url", Response::Unavail)?) {
        Ok(u) => u,
        _ => {
            error!("Could not parse authorization URL");
            return Err(Response::Unavail);
        },
    };
    let token_url = match get_optional(&config, "nss.token_url") {
        Some(v) => match TokenUrl::new(v) {
            Ok(u) => Some(u),
            Err(_) => {
                error!("Could not parse token URL");
                return Err(Response::Unavail);
            }
        },
        None => None,
    };
    let scopes: Vec<&str> = get_or_error(&config, "nss.scopes", Response::Unavail)?;

    let client = BasicClient::new(client_id, client_secret, auth_url, token_url);
    let mut request = client.exchange_client_credentials();
    for scope in scopes {
        request = request.add_scope(Scope::new(scope.to_string()));
    }
    let result = request.request(http_client);

    match result {
            Ok(t) => Ok(t),
            Err(e) => match e {
                RequestTokenError::Request(re) => match re {
                    reqwest::Error::Reqwest(ree) => {
                        error!("Request error requesting token: {}", ree);
                        return Err(Response::Unavail);
                    },
                    reqwest::Error::Http(he) => {
                        error!("HTTP error requesting token: {}", he);
                        return Err(Response::Unavail);
                    },
                    reqwest::Error::Io(ioe) => {
                        error!("IO error requesting token: {}", ioe);
                        return Err(Response::Unavail);
                    },
                    _ => {
                        error!("Unknown error: {}", re);
                        return Err(Response::Unavail);
                    },
                },
                RequestTokenError::ServerResponse(t) => {
                    error!("Authorization server returned error: {}", t);
                    return Err(Response::Unavail);
                },
                RequestTokenError::Parse(pe, _) => {
                    error!("Error parsing response: {}", pe);
                    return Err(Response::Unavail);
                },
                _ => {
                    error!("Unknown error: {}", e);
                    return Err(Response::Unavail);
                },
            },
        }
}

fn do_json_request(config: Config, url: String) -> Result<String, Response> {
    let token = get_bearer_token(config)?;
}

struct OidcPasswd;

impl PasswdHooks for OidcPasswd {
    fn get_all_entries() -> Vec<Passwd> {
        let config = nss_hook_prepare();

        vec![
            Passwd {
                name: "test".to_string(),
                passwd: "x".to_string(),
                uid: 1005,
                gid: 1005,
                gecos: "Test Account".to_string(),
                dir: "/home/test".to_string(),
                shell: "/bin/bash".to_string(),
            }
        ]
    }

    fn get_entry_by_uid(uid: libc::uid_t) -> Option<Passwd> {
        if uid == 1005 {
            return Some(Passwd {
                name: "test".to_string(),
                passwd: "x".to_string(),
                uid: 1005,
                gid: 1005,
                gecos: "Test Account".to_string(),
                dir: "/home/test".to_string(),
                shell: "/bin/bash".to_string(),
            });
        }

        None
    }

    fn get_entry_by_name(name: String) -> Option<Passwd> {
        if name == "test" {
            return Some(Passwd {
                name: "test".to_string(),
                passwd: "x".to_string(),
                uid: 1005,
                gid: 1005,
                gecos: "Test Account".to_string(),
                dir: "/home/test".to_string(),
                shell: "/bin/bash".to_string(),
            });
        }

        None
    }
}

libnss_passwd_hooks!(oidc, OidcPasswd);