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
123
124
125
126
127
128
129
use std::fmt;
use std::str::FromStr;
use std::slice::Iter;
use std::result::Result as StdResult;

use chrono::{Duration, UTC, DateTime};

use dto::{FromDTO, AccessTokenDTO, TokenTypeDTO, FromDTOError};

use error::{Error, Result};

/// Access Token Type
pub type TokenType = TokenTypeDTO;

/// Struct representing an access token.
#[derive(Debug, Clone)]
pub struct AccessToken {
    app_id: String,
    scopes: Vec<Scope>,
    access_token: String,
    token_type: TokenType,
    expiration: DateTime<UTC>,
}

impl AccessToken {
    /// Creates an access token from stored data.
    pub fn from_data(app_id: String, scopes: Vec<Scope>, access_token: String, token_type: TokenTypeDTO, expiration: DateTime<UTC>) -> AccessToken {
        AccessToken {app_id: app_id, scopes: scopes, access_token: access_token, token_type: token_type, expiration: expiration}
    }

    /// Gets the application ID of the token.
    pub fn get_app_id(&self) -> &str {
        &self.app_id
    }

    /// Gets an iterator through the scopes of the token.
    pub fn scopes(&self) -> Iter<Scope> {
        self.scopes.iter()
    }

    /// Gets the token as a string.
    ///
    /// This string will be the one that identifies the client in the API.
    pub fn as_str(&self) -> &str {
        &self.access_token
    }

    /// Gets the type of the token.
    pub fn get_token_type(&self) -> TokenType {
        self.token_type
    }

    /// Gets the expiration time of the token.
    pub fn get_expiration(&self) -> DateTime<UTC> {
        self.expiration
    }

    /// Returns wether the access token expired or not.
    pub fn has_expired(&self) -> bool {
        self.expiration < UTC::now()
    }
}

impl FromDTO<AccessTokenDTO> for AccessToken {
    fn from_dto(dto: AccessTokenDTO) -> StdResult<AccessToken, FromDTOError> {
        let mut scopes = Vec::new();
        for scope in dto.scopes.split(',') {
            scopes.push(match Scope::from_str(scope) {
                Ok(s) => s,
                Err(_) => return Err(FromDTOError {}),
            });
        }
        if scopes.len() == 0 {
            return Err(FromDTOError {});
        }
        let expiry_time = UTC::now() + Duration::seconds(dto.expiration);

        Ok(AccessToken {app_id: dto.app_id, scopes: scopes, access_token: dto.access_token, token_type: dto.token_type, expiration: expiry_time})
    }
}

/// Enum that represents
#[derive(Debug, PartialOrd, PartialEq, Eq, Copy, Clone, RustcDecodable, RustcEncodable)]
pub enum Scope {
    /// Administration scope
    ///
    /// This scope is used for administration purposes, and will not be enabled for public
    /// development accounts.
    Admin,
    /// User scope
    ///
    /// This scope will provide access to user functionality, such as creating transactions and
    /// editing user information. It contains the user ID for which the token is valid.
    User(u64),
    /// Public scope
    ///
    /// This scope is the public scope. Every client will have access to everything provided in the
    /// admin scope.
    Public,
    /// Developer scope
    ///
    /// This scope is used for administration purposes, and will not be enabled for public
    /// development accounts.
    Developer,
}

impl fmt::Display for Scope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl FromStr for Scope {
    type Err = Error;
    fn from_str(s: &str) -> Result<Scope> {
        match s {
            "Admin" => Ok(Scope::Admin),
            "Public" => Ok(Scope::Public),
            "Developer" => Ok(Scope::Developer),
            s => match s.rfind("User:") {
                Some(i) => Ok(Scope::User(match s[i..].parse() {
                    Ok(id) => id,
                    _ => return Err(Error::InvalidScope),
                })),
                _ => Err(Error::InvalidScope),
            },
        }
    }
}