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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// 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.

//! Authentication modules.
//!
//! Usually, accessing OpenStack services requires authentication. This module
//! provides a way to authenticate against an Identity service, as well as
//! simple authentication implementations for standalone use.
//!
//! The usual workflow for connecting to OpenStack API is as follows:
//!
//! 1. Create a suitable authentication method.
//! 2. Populate it with authentication data (credentials, etc).
//! 3. Create a [Cloud](../struct.Cloud.html).
//!
//! # Using password authentication
//!
//! Start with creating an [Identity](struct.Identity.html) object which will
//! guide you through setting all necessary values.
//! [PasswordAuth](struct.PasswordAuth.html) is the actual implementation
//! of the authentication [method](trait.AuthMethod.html) trait.
//!
//! Note that as of now, only project-scoped tokens are supported.
//! An attempt to create unscoped tokens always fails. This restriction may
//! be lifted in the future.
//!
//! # Examples
//!
//! Creating an authentication method using project-scoped tokens:
//!
//! ```rust,no_run
//! use openstack;
//!
//! let auth = openstack::auth::Password::new(
//!         "https://my.cloud.com/identity",
//!         "admin", "pa$$w0rd", "domain.com")
//!     .expect("Invalid authentication URL")
//!     .with_project_scope("project1", "domain.com");
//! let os = openstack::Cloud::new(auth);
//! ```
//!
//! Creating a session and a cloud from environment variables:
//!
//! ```rust,no_run
//! use openstack;
//!
//! let session = openstack::auth::from_env().expect("Failed to authenticate");
//! let os = openstack::Cloud::from(session);
//! ```
//!
//! Creating a dummy authentication method for use against clouds that do not
//! have actual authentication:
//!
//! ```
//! use openstack;
//!
//! let auth = openstack::auth::NoAuth::new("https://my.cloud.com/some-service")
//!     .unwrap();
//! let os = openstack::Cloud::new(auth);
//! ```
//!
//! # Limitations
//!
//! * Only Identity API v3 is supported and planned for support.

mod base;
mod config;
mod identity;
mod simple;

pub use self::base::{AuthMethod, BoxedClone};
pub use self::config::from_config;
pub use self::simple::NoAuth;
pub use self::identity::{Identity, Password};

use std::env;

use super::{Error, ErrorKind, Result};
use super::session::Session;

const MISSING_ENV_VARS: &str =
    "Not all required environment variables were provided";

#[inline]
fn _get_env(name: &str) -> Result<String> {
    env::var(name).map_err(|_| {
        Error::new(ErrorKind::InvalidInput, MISSING_ENV_VARS)
    })
}


/// Create a `Session` from environment variables.
pub fn from_env() -> Result<Session> {
    if let Ok(cloud_name) = env::var("OS_CLOUD") {
        from_config(cloud_name)
    } else {
        let auth_url = _get_env("OS_AUTH_URL")?;
        let user_name = _get_env("OS_USERNAME")?;
        let password = _get_env("OS_PASSWORD")?;
        let user_domain = env::var("OS_USER_DOMAIN_NAME")
            .unwrap_or(String::from("Default"));

        let id = Password::new(&auth_url, user_name, password, user_domain)?;

        let project_name = _get_env("OS_PROJECT_NAME")?;
        let project_domain = env::var("OS_PROJECT_DOMAIN_NAME")
            .unwrap_or(String::from("Default"));

        Ok(Session::new(id.with_project_scope(project_name, project_domain)))
    }
}