Updated bw_rs to Rocket version 0.4-rc1

This commit is contained in:
Daniel García 2018-10-10 20:40:39 +02:00
parent f1b1000600
commit c673370103
No known key found for this signature in database
GPG Key ID: FC8A7D14C3CD543A
31 changed files with 716 additions and 1485 deletions

879
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,18 +5,17 @@ authors = ["Daniel García <dani-garcia@users.noreply.github.com>"]
[dependencies] [dependencies]
# Web framework for nightly with a focus on ease-of-use, expressibility, and speed. # Web framework for nightly with a focus on ease-of-use, expressibility, and speed.
rocket = { version = "0.3.17", features = ["tls"] } rocket = { version = "0.4.0-rc.1", features = ["tls"] }
rocket_codegen = "0.3.17" rocket_contrib = "0.4.0-rc.1"
rocket_contrib = "0.3.17"
# HTTP client # HTTP client
reqwest = "0.9.2" reqwest = "0.9.4"
# multipart/form-data support # multipart/form-data support
multipart = "0.15.3" multipart = "0.15.3"
# WebSockets library # WebSockets library
ws = "0.7.8" ws = "0.7.9"
# MessagePack library # MessagePack library
rmpv = "0.4.0" rmpv = "0.4.0"
@ -25,9 +24,9 @@ rmpv = "0.4.0"
chashmap = "2.2.0" chashmap = "2.2.0"
# A generic serialization/deserialization framework # A generic serialization/deserialization framework
serde = "1.0.79" serde = "1.0.80"
serde_derive = "1.0.79" serde_derive = "1.0.80"
serde_json = "1.0.31" serde_json = "1.0.32"
# A safe, extensible ORM and Query builder # A safe, extensible ORM and Query builder
diesel = { version = "1.3.3", features = ["sqlite", "chrono", "r2d2"] } diesel = { version = "1.3.3", features = ["sqlite", "chrono", "r2d2"] }
@ -37,7 +36,7 @@ diesel_migrations = { version = "1.3.0", features = ["sqlite"] }
libsqlite3-sys = { version = "0.9.3", features = ["bundled"] } libsqlite3-sys = { version = "0.9.3", features = ["bundled"] }
# Crypto library # Crypto library
ring = { version = "= 0.11.0", features = ["rsa_signing"] } ring = { version = "0.13.2", features = ["rsa_signing"] }
# UUID generation # UUID generation
uuid = { version = "0.7.1", features = ["v4"] } uuid = { version = "0.7.1", features = ["v4"] }
@ -52,7 +51,7 @@ oath = "0.10.2"
data-encoding = "2.1.1" data-encoding = "2.1.1"
# JWT library # JWT library
jsonwebtoken = "= 4.0.1" jsonwebtoken = "5.0.1"
# U2F library # U2F library
u2f = "0.1.2" u2f = "0.1.2"
@ -70,17 +69,18 @@ num-derive = "0.2.3"
# Email libraries # Email libraries
lettre = "0.9.0" lettre = "0.9.0"
lettre_email = "0.9.0" lettre_email = "0.9.0"
native-tls = "0.2.1" native-tls = "0.2.2"
# Number encoding library # Number encoding library
byteorder = "1.2.6" byteorder = "1.2.7"
[patch.crates-io] [patch.crates-io]
# Make jwt use ring 0.11, to match rocket # Add support for Timestamp type
jsonwebtoken = { path = "libs/jsonwebtoken" }
rmp = { git = 'https://github.com/dani-garcia/msgpack-rust' } rmp = { git = 'https://github.com/dani-garcia/msgpack-rust' }
# Use new native_tls version 0.2
lettre = { git = 'https://github.com/lettre/lettre', rev = 'c988b1760ad81' } lettre = { git = 'https://github.com/lettre/lettre', rev = 'c988b1760ad81' }
lettre_email = { git = 'https://github.com/lettre/lettre', rev = 'c988b1760ad81' } lettre_email = { git = 'https://github.com/lettre/lettre', rev = 'c988b1760ad81' }
# Version 0.1.2 from crates.io lacks a commit that fixes a certificate error # Version 0.1.2 from crates.io lacks a commit that fixes a certificate error
u2f = { git = 'https://github.com/wisespace-io/u2f-rs', rev = '193de35093a44' } u2f = { git = 'https://github.com/wisespace-io/u2f-rs', rev = '75b9fa5afb4c5' }

View File

@ -1,20 +0,0 @@
[package]
name = "jsonwebtoken"
version = "4.0.1"
authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"]
license = "MIT"
readme = "README.md"
description = "Create and parse JWT in a strongly typed way."
homepage = "https://github.com/Keats/rust-jwt"
repository = "https://github.com/Keats/rust-jwt"
keywords = ["jwt", "web", "api", "token", "json"]
[dependencies]
error-chain = { version = "0.11", default-features = false }
serde_json = "1.0"
serde_derive = "1.0"
serde = "1.0"
ring = { version = "0.11.0", features = ["rsa_signing", "dev_urandom_fallback"] }
base64 = "0.9"
untrusted = "0.5"
chrono = "0.4"

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Vincent Prouillet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,120 +0,0 @@
use std::sync::Arc;
use base64;
use ring::{rand, digest, hmac, signature};
use ring::constant_time::verify_slices_are_equal;
use untrusted;
use errors::{Result, ErrorKind};
/// The algorithms supported for signing/verifying
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum Algorithm {
/// HMAC using SHA-256
HS256,
/// HMAC using SHA-384
HS384,
/// HMAC using SHA-512
HS512,
/// RSASSA-PKCS1-v1_5 using SHA-256
RS256,
/// RSASSA-PKCS1-v1_5 using SHA-384
RS384,
/// RSASSA-PKCS1-v1_5 using SHA-512
RS512,
}
/// The actual HS signing + encoding
fn sign_hmac(alg: &'static digest::Algorithm, key: &[u8], signing_input: &str) -> Result<String> {
let signing_key = hmac::SigningKey::new(alg, key);
let digest = hmac::sign(&signing_key, signing_input.as_bytes());
Ok(
base64::encode_config::<hmac::Signature>(&digest, base64::URL_SAFE_NO_PAD)
)
}
/// The actual RSA signing + encoding
/// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html
fn sign_rsa(alg: Algorithm, key: &[u8], signing_input: &str) -> Result<String> {
let ring_alg = match alg {
Algorithm::RS256 => &signature::RSA_PKCS1_SHA256,
Algorithm::RS384 => &signature::RSA_PKCS1_SHA384,
Algorithm::RS512 => &signature::RSA_PKCS1_SHA512,
_ => unreachable!(),
};
let key_pair = Arc::new(
signature::RSAKeyPair::from_der(untrusted::Input::from(key))
.map_err(|_| ErrorKind::InvalidKey)?
);
let mut signing_state = signature::RSASigningState::new(key_pair)
.map_err(|_| ErrorKind::InvalidKey)?;
let mut signature = vec![0; signing_state.key_pair().public_modulus_len()];
let rng = rand::SystemRandom::new();
signing_state.sign(ring_alg, &rng, signing_input.as_bytes(), &mut signature)
.map_err(|_| ErrorKind::InvalidKey)?;
Ok(
base64::encode_config::<[u8]>(&signature, base64::URL_SAFE_NO_PAD)
)
}
/// Take the payload of a JWT, sign it using the algorithm given and return
/// the base64 url safe encoded of the result.
///
/// Only use this function if you want to do something other than JWT.
pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<String> {
match algorithm {
Algorithm::HS256 => sign_hmac(&digest::SHA256, key, signing_input),
Algorithm::HS384 => sign_hmac(&digest::SHA384, key, signing_input),
Algorithm::HS512 => sign_hmac(&digest::SHA512, key, signing_input),
Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => sign_rsa(algorithm, key, signing_input),
// TODO: if PKCS1 is made prublic, remove the line above and uncomment below
// Algorithm::RS256 => sign_rsa(&signature::RSA_PKCS1_SHA256, key, signing_input),
// Algorithm::RS384 => sign_rsa(&signature::RSA_PKCS1_SHA384, key, signing_input),
// Algorithm::RS512 => sign_rsa(&signature::RSA_PKCS1_SHA512, key, signing_input),
}
}
/// See Ring RSA docs for more details
fn verify_rsa(alg: &signature::RSAParameters, signature: &str, signing_input: &str, key: &[u8]) -> Result<bool> {
let signature_bytes = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?;
let public_key_der = untrusted::Input::from(key);
let message = untrusted::Input::from(signing_input.as_bytes());
let expected_signature = untrusted::Input::from(signature_bytes.as_slice());
let res = signature::verify(alg, public_key_der, message, expected_signature);
Ok(res.is_ok())
}
/// Compares the signature given with a re-computed signature for HMAC or using the public key
/// for RSA.
///
/// Only use this function if you want to do something other than JWT.
///
/// `signature` is the signature part of a jwt (text after the second '.')
///
/// `signing_input` is base64(header) + "." + base64(claims)
pub fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<bool> {
match algorithm {
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
// we just re-sign the data with the key and compare if they are equal
let signed = sign(signing_input, key, algorithm)?;
Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok())
},
Algorithm::RS256 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA256, signature, signing_input, key),
Algorithm::RS384 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA384, signature, signing_input, key),
Algorithm::RS512 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA512, signature, signing_input, key),
}
}
impl Default for Algorithm {
fn default() -> Self {
Algorithm::HS256
}
}

View File

@ -1,68 +0,0 @@
use base64;
use serde_json;
use ring;
error_chain! {
errors {
/// When a token doesn't have a valid JWT shape
InvalidToken {
description("invalid token")
display("Invalid token")
}
/// When the signature doesn't match
InvalidSignature {
description("invalid signature")
display("Invalid signature")
}
/// When the secret given is not a valid RSA key
InvalidKey {
description("invalid key")
display("Invalid Key")
}
// Validation error
/// When a tokens `exp` claim indicates that it has expired
ExpiredSignature {
description("expired signature")
display("Expired Signature")
}
/// When a tokens `iss` claim does not match the expected issuer
InvalidIssuer {
description("invalid issuer")
display("Invalid Issuer")
}
/// When a tokens `aud` claim does not match one of the expected audience values
InvalidAudience {
description("invalid audience")
display("Invalid Audience")
}
/// When a tokens `aud` claim does not match one of the expected audience values
InvalidSubject {
description("invalid subject")
display("Invalid Subject")
}
/// When a tokens `iat` claim is in the future
InvalidIssuedAt {
description("invalid issued at")
display("Invalid Issued At")
}
/// When a tokens nbf claim represents a time in the future
ImmatureSignature {
description("immature signature")
display("Immature Signature")
}
/// When the algorithm in the header doesn't match the one passed to `decode`
InvalidAlgorithm {
description("Invalid algorithm")
display("Invalid Algorithm")
}
}
foreign_links {
Unspecified(ring::error::Unspecified) #[doc = "An error happened while signing/verifying a token with RSA"];
Base64(base64::DecodeError) #[doc = "An error happened while decoding some base64 text"];
Json(serde_json::Error) #[doc = "An error happened while serializing/deserializing JSON"];
Utf8(::std::string::FromUtf8Error) #[doc = "An error happened while trying to convert the result of base64 decoding to a String"];
}
}

View File

@ -1,64 +0,0 @@
use crypto::Algorithm;
/// A basic JWT header, the alg defaults to HS256 and typ is automatically
/// set to `JWT`. All the other fields are optional.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Header {
/// The type of JWS: it can only be "JWT" here
///
/// Defined in [RFC7515#4.1.9](https://tools.ietf.org/html/rfc7515#section-4.1.9).
#[serde(skip_serializing_if = "Option::is_none")]
pub typ: Option<String>,
/// The algorithm used
///
/// Defined in [RFC7515#4.1.1](https://tools.ietf.org/html/rfc7515#section-4.1.1).
pub alg: Algorithm,
/// Content type
///
/// Defined in [RFC7519#5.2](https://tools.ietf.org/html/rfc7519#section-5.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub cty: Option<String>,
/// JSON Key URL
///
/// Defined in [RFC7515#4.1.2](https://tools.ietf.org/html/rfc7515#section-4.1.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub jku: Option<String>,
/// Key ID
///
/// Defined in [RFC7515#4.1.4](https://tools.ietf.org/html/rfc7515#section-4.1.4).
#[serde(skip_serializing_if = "Option::is_none")]
pub kid: Option<String>,
/// X.509 URL
///
/// Defined in [RFC7515#4.1.5](https://tools.ietf.org/html/rfc7515#section-4.1.5).
#[serde(skip_serializing_if = "Option::is_none")]
pub x5u: Option<String>,
/// X.509 certificate thumbprint
///
/// Defined in [RFC7515#4.1.7](https://tools.ietf.org/html/rfc7515#section-4.1.7).
#[serde(skip_serializing_if = "Option::is_none")]
pub x5t: Option<String>,
}
impl Header {
/// Returns a JWT header with the algorithm given
pub fn new(algorithm: Algorithm) -> Header {
Header {
typ: Some("JWT".to_string()),
alg: algorithm,
cty: None,
jku: None,
kid: None,
x5u: None,
x5t: None,
}
}
}
impl Default for Header {
/// Returns a JWT header using the default Algorithm, HS256
fn default() -> Self {
Header::new(Algorithm::default())
}
}

View File

@ -1,142 +0,0 @@
//! Create and parses JWT (JSON Web Tokens)
//!
//! Documentation: [stable](https://docs.rs/jsonwebtoken/)
#![recursion_limit = "300"]
#![deny(missing_docs)]
#![allow(unused_doc_comments)]
#![allow(renamed_and_removed_lints)]
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
extern crate base64;
extern crate ring;
extern crate untrusted;
extern crate chrono;
/// All the errors, generated using error-chain
pub mod errors;
mod header;
mod crypto;
mod serialization;
mod validation;
pub use header::Header;
pub use crypto::{
Algorithm,
sign,
verify,
};
pub use validation::Validation;
pub use serialization::TokenData;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use errors::{Result, ErrorKind};
use serialization::{from_jwt_part, from_jwt_part_claims, to_jwt_part};
use validation::{validate};
/// Encode the header and claims given and sign the payload using the algorithm from the header and the key
///
/// ```rust,ignore
/// #[macro_use]
/// extern crate serde_derive;
/// use jsonwebtoken::{encode, Algorithm, Header};
///
/// /// #[derive(Debug, Serialize, Deserialize)]
/// struct Claims {
/// sub: String,
/// company: String
/// }
///
/// let my_claims = Claims {
/// sub: "b@b.com".to_owned(),
/// company: "ACME".to_owned()
/// };
///
/// // my_claims is a struct that implements Serialize
/// // This will create a JWT using HS256 as algorithm
/// let token = encode(&Header::default(), &my_claims, "secret".as_ref()).unwrap();
/// ```
pub fn encode<T: Serialize>(header: &Header, claims: &T, key: &[u8]) -> Result<String> {
let encoded_header = to_jwt_part(&header)?;
let encoded_claims = to_jwt_part(&claims)?;
let signing_input = [encoded_header.as_ref(), encoded_claims.as_ref()].join(".");
let signature = sign(&*signing_input, key.as_ref(), header.alg)?;
Ok([signing_input, signature].join("."))
}
/// Used in decode: takes the result of a rsplit and ensure we only get 2 parts
/// Errors if we don't
macro_rules! expect_two {
($iter:expr) => {{
let mut i = $iter;
match (i.next(), i.next(), i.next()) {
(Some(first), Some(second), None) => (first, second),
_ => return Err(ErrorKind::InvalidToken.into())
}
}}
}
/// Decode a token into a struct containing 2 fields: `claims` and `header`.
///
/// If the token or its signature is invalid or the claims fail validation, it will return an error.
///
/// ```rust,ignore
/// #[macro_use]
/// extern crate serde_derive;
/// use jsonwebtoken::{decode, Validation, Algorithm};
///
/// #[derive(Debug, Serialize, Deserialize)]
/// struct Claims {
/// sub: String,
/// company: String
/// }
///
/// let token = "a.jwt.token".to_string();
/// // Claims is a struct that implements Deserialize
/// let token_data = decode::<Claims>(&token, "secret", &Validation::new(Algorithm::HS256));
/// ```
pub fn decode<T: DeserializeOwned>(token: &str, key: &[u8], validation: &Validation) -> Result<TokenData<T>> {
let (signature, signing_input) = expect_two!(token.rsplitn(2, '.'));
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
let header: Header = from_jwt_part(header)?;
if !verify(signature, signing_input, key, header.alg)? {
return Err(ErrorKind::InvalidSignature.into());
}
if !validation.algorithms.contains(&header.alg) {
return Err(ErrorKind::InvalidAlgorithm.into());
}
let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?;
validate(&claims_map, validation)?;
Ok(TokenData { header: header, claims: decoded_claims })
}
/// Decode a token and return the Header. This is not doing any kind of validation: it is meant to be
/// used when you don't know which `alg` the token is using and want to find out.
///
/// If the token has an invalid format, it will return an error.
///
/// ```rust,ignore
/// use jsonwebtoken::decode_header;
///
/// let token = "a.jwt.token".to_string();
/// let header = decode_header(&token);
/// ```
pub fn decode_header(token: &str) -> Result<Header> {
let (_, signing_input) = expect_two!(token.rsplitn(2, '.'));
let (_, header) = expect_two!(signing_input.rsplitn(2, '.'));
from_jwt_part(header)
}

View File

@ -1,42 +0,0 @@
use base64;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use serde_json::{from_str, to_string, Value};
use serde_json::map::Map;
use errors::{Result};
use header::Header;
/// The return type of a successful call to decode
#[derive(Debug)]
pub struct TokenData<T> {
/// The decoded JWT header
pub header: Header,
/// The decoded JWT claims
pub claims: T
}
/// Serializes to JSON and encodes to base64
pub fn to_jwt_part<T: Serialize>(input: &T) -> Result<String> {
let encoded = to_string(input)?;
Ok(base64::encode_config(encoded.as_bytes(), base64::URL_SAFE_NO_PAD))
}
/// Decodes from base64 and deserializes from JSON to a struct
pub fn from_jwt_part<B: AsRef<str>, T: DeserializeOwned>(encoded: B) -> Result<T> {
let decoded = base64::decode_config(encoded.as_ref(), base64::URL_SAFE_NO_PAD)?;
let s = String::from_utf8(decoded)?;
Ok(from_str(&s)?)
}
/// Decodes from base64 and deserializes from JSON to a struct AND a hashmap
pub fn from_jwt_part_claims<B: AsRef<str>, T: DeserializeOwned>(encoded: B) -> Result<(T, Map<String, Value>)> {
let decoded = base64::decode_config(encoded.as_ref(), base64::URL_SAFE_NO_PAD)?;
let s = String::from_utf8(decoded)?;
let claims: T = from_str(&s)?;
let map: Map<_,_> = from_str(&s)?;
Ok((claims, map))
}

View File

@ -1,377 +0,0 @@
use chrono::Utc;
use serde::ser::Serialize;
use serde_json::{Value, from_value, to_value};
use serde_json::map::Map;
use errors::{Result, ErrorKind};
use crypto::Algorithm;
/// Contains the various validations that are applied after decoding a token.
///
/// All time validation happen on UTC timestamps.
///
/// ```rust
/// use jsonwebtoken::Validation;
///
/// // Default value
/// let validation = Validation::default();
///
/// // Changing one parameter
/// let mut validation = Validation {leeway: 60, ..Default::default()};
///
/// // Setting audience
/// let mut validation = Validation::default();
/// validation.set_audience(&"Me"); // string
/// validation.set_audience(&["Me", "You"]); // array of strings
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct Validation {
/// Add some leeway (in seconds) to the `exp`, `iat` and `nbf` validation to
/// account for clock skew.
///
/// Defaults to `0`.
pub leeway: i64,
/// Whether to validate the `exp` field.
///
/// It will return an error if the time in the `exp` field is past.
///
/// Defaults to `true`.
pub validate_exp: bool,
/// Whether to validate the `iat` field.
///
/// It will return an error if the time in the `iat` field is in the future.
///
/// Defaults to `true`.
pub validate_iat: bool,
/// Whether to validate the `nbf` field.
///
/// It will return an error if the current timestamp is before the time in the `nbf` field.
///
/// Defaults to `true`.
pub validate_nbf: bool,
/// If it contains a value, the validation will check that the `aud` field is the same as the
/// one provided and will error otherwise.
/// Since `aud` can be either a String or a Vec<String> in the JWT spec, you will need to use
/// the [set_audience](struct.Validation.html#method.set_audience) method to set it.
///
/// Defaults to `None`.
pub aud: Option<Value>,
/// If it contains a value, the validation will check that the `iss` field is the same as the
/// one provided and will error otherwise.
///
/// Defaults to `None`.
pub iss: Option<String>,
/// If it contains a value, the validation will check that the `sub` field is the same as the
/// one provided and will error otherwise.
///
/// Defaults to `None`.
pub sub: Option<String>,
/// If it contains a value, the validation will check that the `alg` of the header is contained
/// in the ones provided and will error otherwise.
///
/// Defaults to `vec![Algorithm::HS256]`.
pub algorithms: Vec<Algorithm>,
}
impl Validation {
/// Create a default validation setup allowing the given alg
pub fn new(alg: Algorithm) -> Validation {
let mut validation = Validation::default();
validation.algorithms = vec![alg];
validation
}
/// Since `aud` can be either a String or an array of String in the JWT spec, this method will take
/// care of serializing the value.
pub fn set_audience<T: Serialize>(&mut self, audience: &T) {
self.aud = Some(to_value(audience).unwrap());
}
}
impl Default for Validation {
fn default() -> Validation {
Validation {
leeway: 0,
validate_exp: true,
validate_iat: true,
validate_nbf: true,
iss: None,
sub: None,
aud: None,
algorithms: vec![Algorithm::HS256],
}
}
}
pub fn validate(claims: &Map<String, Value>, options: &Validation) -> Result<()> {
let now = Utc::now().timestamp();
if let Some(iat) = claims.get("iat") {
if options.validate_iat && from_value::<i64>(iat.clone())? > now + options.leeway {
return Err(ErrorKind::InvalidIssuedAt.into());
}
}
if let Some(exp) = claims.get("exp") {
if options.validate_exp && from_value::<i64>(exp.clone())? < now - options.leeway {
return Err(ErrorKind::ExpiredSignature.into());
}
}
if let Some(nbf) = claims.get("nbf") {
if options.validate_nbf && from_value::<i64>(nbf.clone())? > now + options.leeway {
return Err(ErrorKind::ImmatureSignature.into());
}
}
if let Some(iss) = claims.get("iss") {
if let Some(ref correct_iss) = options.iss {
if from_value::<String>(iss.clone())? != *correct_iss {
return Err(ErrorKind::InvalidIssuer.into());
}
}
}
if let Some(sub) = claims.get("sub") {
if let Some(ref correct_sub) = options.sub {
if from_value::<String>(sub.clone())? != *correct_sub {
return Err(ErrorKind::InvalidSubject.into());
}
}
}
if let Some(aud) = claims.get("aud") {
if let Some(ref correct_aud) = options.aud {
if aud != correct_aud {
return Err(ErrorKind::InvalidAudience.into());
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use serde_json::{to_value};
use serde_json::map::Map;
use chrono::Utc;
use super::{validate, Validation};
use errors::ErrorKind;
#[test]
fn iat_in_past_ok() {
let mut claims = Map::new();
claims.insert("iat".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap());
let res = validate(&claims, &Validation::default());
assert!(res.is_ok());
}
#[test]
fn iat_in_future_fails() {
let mut claims = Map::new();
claims.insert("iat".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap());
let res = validate(&claims, &Validation::default());
assert!(res.is_err());
match res.unwrap_err().kind() {
&ErrorKind::InvalidIssuedAt => (),
_ => assert!(false),
};
}
#[test]
fn iat_in_future_but_in_leeway_ok() {
let mut claims = Map::new();
claims.insert("iat".to_string(), to_value(Utc::now().timestamp() + 50).unwrap());
let validation = Validation {
leeway: 1000 * 60,
..Default::default()
};
let res = validate(&claims, &validation);
assert!(res.is_ok());
}
#[test]
fn exp_in_future_ok() {
let mut claims = Map::new();
claims.insert("exp".to_string(), to_value(Utc::now().timestamp() + 10000).unwrap());
let res = validate(&claims, &Validation::default());
assert!(res.is_ok());
}
#[test]
fn exp_in_past_fails() {
let mut claims = Map::new();
claims.insert("exp".to_string(), to_value(Utc::now().timestamp() - 100000).unwrap());
let res = validate(&claims, &Validation::default());
assert!(res.is_err());
match res.unwrap_err().kind() {
&ErrorKind::ExpiredSignature => (),
_ => assert!(false),
};
}
#[test]
fn exp_in_past_but_in_leeway_ok() {
let mut claims = Map::new();
claims.insert("exp".to_string(), to_value(Utc::now().timestamp() - 500).unwrap());
let validation = Validation {
leeway: 1000 * 60,
..Default::default()
};
let res = validate(&claims, &validation);
assert!(res.is_ok());
}
#[test]
fn nbf_in_past_ok() {
let mut claims = Map::new();
claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap());
let res = validate(&claims, &Validation::default());
assert!(res.is_ok());
}
#[test]
fn nbf_in_future_fails() {
let mut claims = Map::new();
claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap());
let res = validate(&claims, &Validation::default());
assert!(res.is_err());
match res.unwrap_err().kind() {
&ErrorKind::ImmatureSignature => (),
_ => assert!(false),
};
}
#[test]
fn nbf_in_future_but_in_leeway_ok() {
let mut claims = Map::new();
claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() + 500).unwrap());
let validation = Validation {
leeway: 1000 * 60,
..Default::default()
};
let res = validate(&claims, &validation);
assert!(res.is_ok());
}
#[test]
fn iss_ok() {
let mut claims = Map::new();
claims.insert("iss".to_string(), to_value("Keats").unwrap());
let validation = Validation {
iss: Some("Keats".to_string()),
..Default::default()
};
let res = validate(&claims, &validation);
assert!(res.is_ok());
}
#[test]
fn iss_not_matching_fails() {
let mut claims = Map::new();
claims.insert("iss".to_string(), to_value("Hacked").unwrap());
let validation = Validation {
iss: Some("Keats".to_string()),
..Default::default()
};
let res = validate(&claims, &validation);
assert!(res.is_err());
match res.unwrap_err().kind() {
&ErrorKind::InvalidIssuer => (),
_ => assert!(false),
};
}
#[test]
fn sub_ok() {
let mut claims = Map::new();
claims.insert("sub".to_string(), to_value("Keats").unwrap());
let validation = Validation {
sub: Some("Keats".to_string()),
..Default::default()
};
let res = validate(&claims, &validation);
assert!(res.is_ok());
}
#[test]
fn sub_not_matching_fails() {
let mut claims = Map::new();
claims.insert("sub".to_string(), to_value("Hacked").unwrap());
let validation = Validation {
sub: Some("Keats".to_string()),
..Default::default()
};
let res = validate(&claims, &validation);
assert!(res.is_err());
match res.unwrap_err().kind() {
&ErrorKind::InvalidSubject => (),
_ => assert!(false),
};
}
#[test]
fn aud_string_ok() {
let mut claims = Map::new();
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
let mut validation = Validation::default();
validation.set_audience(&"Everyone");
let res = validate(&claims, &validation);
assert!(res.is_ok());
}
#[test]
fn aud_array_of_string_ok() {
let mut claims = Map::new();
claims.insert("aud".to_string(), to_value(["UserA", "UserB"]).unwrap());
let mut validation = Validation::default();
validation.set_audience(&["UserA", "UserB"]);
let res = validate(&claims, &validation);
assert!(res.is_ok());
}
#[test]
fn aud_type_mismatch_fails() {
let mut claims = Map::new();
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
let mut validation = Validation::default();
validation.set_audience(&["UserA", "UserB"]);
let res = validate(&claims, &validation);
assert!(res.is_err());
match res.unwrap_err().kind() {
&ErrorKind::InvalidAudience => (),
_ => assert!(false),
};
}
#[test]
fn aud_correct_type_not_matching_fails() {
let mut claims = Map::new();
claims.insert("aud".to_string(), to_value("Everyone").unwrap());
let mut validation = Validation::default();
validation.set_audience(&"None");
let res = validate(&claims, &validation);
assert!(res.is_err());
match res.unwrap_err().kind() {
&ErrorKind::InvalidAudience => (),
_ => assert!(false),
};
}
}

View File

@ -1 +1 @@
nightly-2018-10-03 nightly-2018-10-31

View File

@ -1,4 +1,4 @@
use rocket_contrib::Json; use rocket_contrib::json::Json;
use db::DbConn; use db::DbConn;
use db::models::*; use db::models::*;
@ -9,6 +9,29 @@ use mail;
use CONFIG; use CONFIG;
use rocket::Route;
pub fn routes() -> Vec<Route> {
routes![
register,
profile,
put_profile,
post_profile,
get_public_keys,
post_keys,
post_password,
post_kdf,
post_sstamp,
post_email_token,
post_email,
delete_account,
post_delete_account,
revision_date,
password_hint,
prelogin,
]
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
struct RegisterData { struct RegisterData {

View File

@ -1,35 +1,75 @@
use std::path::Path;
use std::collections::HashSet; use std::collections::HashSet;
use std::path::Path;
use rocket::State;
use rocket::Data;
use rocket::http::ContentType; use rocket::http::ContentType;
use rocket::{request::Form, Data, Route, State};
use rocket_contrib::{Json, Value}; use rocket_contrib::json::Json;
use serde_json::Value;
use multipart::server::{Multipart, SaveResult};
use multipart::server::save::SavedData; use multipart::server::save::SavedData;
use multipart::server::{Multipart, SaveResult};
use data_encoding::HEXLOWER; use data_encoding::HEXLOWER;
use db::DbConn;
use db::models::*; use db::models::*;
use db::DbConn;
use crypto; use crypto;
use api::{self, PasswordData, JsonResult, EmptyResult, JsonUpcase, WebSocketUsers, UpdateType}; use api::{self, EmptyResult, JsonResult, JsonUpcase, PasswordData, UpdateType, WebSocketUsers};
use auth::Headers; use auth::Headers;
use CONFIG; use CONFIG;
#[derive(FromForm)] pub fn routes() -> Vec<Route> {
#[allow(non_snake_case)] routes![
struct SyncData { sync,
excludeDomains: bool, get_ciphers,
get_cipher,
get_cipher_admin,
get_cipher_details,
post_ciphers,
put_cipher_admin,
post_ciphers_admin,
post_ciphers_import,
post_attachment,
post_attachment_admin,
post_attachment_share,
delete_attachment_post,
delete_attachment_post_admin,
delete_attachment,
delete_attachment_admin,
post_cipher_admin,
post_cipher_share,
put_cipher_share,
put_cipher_share_seleted,
post_cipher,
put_cipher,
delete_cipher_post,
delete_cipher_post_admin,
delete_cipher,
delete_cipher_admin,
delete_cipher_selected,
delete_cipher_selected_post,
delete_all,
move_cipher_selected,
move_cipher_selected_put,
post_collections_update,
post_collections_admin,
put_collections_admin,
]
} }
#[get("/sync?<data>")] #[derive(FromForm, Default)]
fn sync(data: SyncData, headers: Headers, conn: DbConn) -> JsonResult { struct SyncData {
#[form(field = "excludeDomains")]
exclude_domains: bool, // Default: 'false'
}
#[get("/sync?<data..>")]
fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
let user_json = headers.user.to_json(&conn); let user_json = headers.user.to_json(&conn);
let folders = Folder::find_by_user(&headers.user.uuid, &conn); let folders = Folder::find_by_user(&headers.user.uuid, &conn);
@ -41,7 +81,7 @@ fn sync(data: SyncData, headers: Headers, conn: DbConn) -> JsonResult {
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect(); let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
let domains_json = if data.excludeDomains { Value::Null } else { api::core::get_eq_domains(headers).unwrap().into_inner() }; let domains_json = if data.exclude_domains { Value::Null } else { api::core::get_eq_domains(headers).unwrap().into_inner() };
Ok(Json(json!({ Ok(Json(json!({
"Profile": user_json, "Profile": user_json,
@ -53,14 +93,6 @@ fn sync(data: SyncData, headers: Headers, conn: DbConn) -> JsonResult {
}))) })))
} }
#[get("/sync")]
fn sync_no_query(headers: Headers, conn: DbConn) -> JsonResult {
let sync_data = SyncData {
excludeDomains: false,
};
sync(sync_data, headers, conn)
}
#[get("/ciphers")] #[get("/ciphers")]
fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult { fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
@ -695,8 +727,7 @@ fn delete_all(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn, ws
for f in Folder::find_by_user(&user.uuid, &conn) { for f in Folder::find_by_user(&user.uuid, &conn) {
if f.delete(&conn).is_err() { if f.delete(&conn).is_err() {
err!("Failed deleting folder") err!("Failed deleting folder")
} } else {
else {
ws.send_folder_update(UpdateType::SyncFolderCreate, &f); ws.send_folder_update(UpdateType::SyncFolderCreate, &f);
} }
} }

View File

@ -1,5 +1,6 @@
use rocket::State; use rocket::State;
use rocket_contrib::{Json, Value}; use rocket_contrib::json::Json;
use serde_json::Value;
use db::DbConn; use db::DbConn;
use db::models::*; use db::models::*;
@ -7,6 +8,20 @@ use db::models::*;
use api::{JsonResult, EmptyResult, JsonUpcase, WebSocketUsers, UpdateType}; use api::{JsonResult, EmptyResult, JsonUpcase, WebSocketUsers, UpdateType};
use auth::Headers; use auth::Headers;
use rocket::Route;
pub fn routes() -> Vec<Route> {
routes![
get_folders,
get_folder,
post_folders,
post_folder,
put_folder,
delete_folder_post,
delete_folder,
]
}
#[get("/folders")] #[get("/folders")]
fn get_folders(headers: Headers, conn: DbConn) -> JsonResult { fn get_folders(headers: Headers, conn: DbConn) -> JsonResult {
let folders = Folder::find_by_user(&headers.user.uuid, &conn); let folders = Folder::find_by_user(&headers.user.uuid, &conn);

View File

@ -4,126 +4,25 @@ mod folders;
mod organizations; mod organizations;
pub(crate) mod two_factor; pub(crate) mod two_factor;
use self::accounts::*;
use self::ciphers::*;
use self::folders::*;
use self::organizations::*;
use self::two_factor::*;
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![ let mut mod_routes = routes![
register,
profile,
put_profile,
post_profile,
get_public_keys,
post_keys,
post_password,
post_kdf,
post_sstamp,
post_email_token,
post_email,
delete_account,
post_delete_account,
revision_date,
password_hint,
prelogin,
sync,
sync_no_query,
get_ciphers,
get_cipher,
get_cipher_admin,
get_cipher_details,
post_ciphers,
put_cipher_admin,
post_ciphers_admin,
post_ciphers_import,
post_attachment,
post_attachment_admin,
post_attachment_share,
delete_attachment_post,
delete_attachment_post_admin,
delete_attachment,
delete_attachment_admin,
post_cipher_admin,
post_cipher_share,
put_cipher_share,
put_cipher_share_seleted,
post_cipher,
put_cipher,
delete_cipher_post,
delete_cipher_post_admin,
delete_cipher,
delete_cipher_admin,
delete_cipher_selected,
delete_cipher_selected_post,
delete_all,
move_cipher_selected,
move_cipher_selected_put,
get_folders,
get_folder,
post_folders,
post_folder,
put_folder,
delete_folder_post,
delete_folder,
get_twofactor,
get_recover,
recover,
disable_twofactor,
disable_twofactor_put,
generate_authenticator,
activate_authenticator,
activate_authenticator_put,
generate_u2f,
activate_u2f,
activate_u2f_put,
get_organization,
create_organization,
delete_organization,
post_delete_organization,
leave_organization,
get_user_collections,
get_org_collections,
get_org_collection_detail,
get_collection_users,
put_organization,
post_organization,
post_organization_collections,
delete_organization_collection_user,
post_organization_collection_delete_user,
post_organization_collection_update,
put_organization_collection_update,
delete_organization_collection,
post_organization_collection_delete,
post_collections_update,
post_collections_admin,
put_collections_admin,
get_org_details,
get_org_users,
send_invite,
confirm_invite,
get_user,
edit_user,
put_organization_user,
delete_user,
post_delete_user,
post_reinvite_user,
post_org_import,
clear_device_token, clear_device_token,
put_device_token, put_device_token,
get_eq_domains, get_eq_domains,
post_eq_domains, post_eq_domains,
put_eq_domains, put_eq_domains,
];
] let mut routes = Vec::new();
routes.append(&mut accounts::routes());
routes.append(&mut ciphers::routes());
routes.append(&mut folders::routes());
routes.append(&mut organizations::routes());
routes.append(&mut two_factor::routes());
routes.append(&mut mod_routes);
routes
} }
/// ///
@ -132,7 +31,8 @@ pub fn routes() -> Vec<Route> {
use rocket::Route; use rocket::Route;
use rocket_contrib::{Json, Value}; use rocket_contrib::json::Json;
use serde_json::Value;
use db::DbConn; use db::DbConn;
use db::models::*; use db::models::*;
@ -141,8 +41,8 @@ use api::{JsonResult, EmptyResult, JsonUpcase};
use auth::Headers; use auth::Headers;
#[put("/devices/identifier/<uuid>/clear-token", data = "<data>")] #[put("/devices/identifier/<uuid>/clear-token", data = "<data>")]
fn clear_device_token(uuid: String, data: Json<Value>, headers: Headers, conn: DbConn) -> EmptyResult { fn clear_device_token(uuid: String, data: JsonUpcase<Value>, headers: Headers, conn: DbConn) -> EmptyResult {
let _data: Value = data.into_inner(); let _data: Value = data.into_inner().data;
let device = match Device::find_by_uuid(&uuid, &conn) { let device = match Device::find_by_uuid(&uuid, &conn) {
Some(device) => device, Some(device) => device,
@ -160,8 +60,8 @@ fn clear_device_token(uuid: String, data: Json<Value>, headers: Headers, conn: D
} }
#[put("/devices/identifier/<uuid>/token", data = "<data>")] #[put("/devices/identifier/<uuid>/token", data = "<data>")]
fn put_device_token(uuid: String, data: Json<Value>, headers: Headers, conn: DbConn) -> JsonResult { fn put_device_token(uuid: String, data: JsonUpcase<Value>, headers: Headers, conn: DbConn) -> JsonResult {
let _data: Value = data.into_inner(); let _data: Value = data.into_inner().data;
let device = match Device::find_by_uuid(&uuid, &conn) { let device = match Device::find_by_uuid(&uuid, &conn) {
Some(device) => device, Some(device) => device,

View File

@ -1,5 +1,8 @@
use rocket::State; use rocket::State;
use rocket_contrib::{Json, Value}; use rocket::request::Form;
use rocket_contrib::json::Json;
use serde_json::Value;
use CONFIG; use CONFIG;
use db::DbConn; use db::DbConn;
use db::models::*; use db::models::*;
@ -9,6 +12,42 @@ use auth::{Headers, AdminHeaders, OwnerHeaders};
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use rocket::Route;
pub fn routes() -> Vec<Route> {
routes![
get_organization,
create_organization,
delete_organization,
post_delete_organization,
leave_organization,
get_user_collections,
get_org_collections,
get_org_collection_detail,
get_collection_users,
put_organization,
post_organization,
post_organization_collections,
delete_organization_collection_user,
post_organization_collection_delete_user,
post_organization_collection_update,
put_organization_collection_update,
delete_organization_collection,
post_organization_collection_delete,
get_org_details,
get_org_users,
send_invite,
confirm_invite,
get_user,
edit_user,
put_organization_user,
delete_user,
post_delete_user,
post_reinvite_user,
post_org_import,
]
}
#[derive(Deserialize)] #[derive(Deserialize)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -315,14 +354,14 @@ fn get_collection_users(org_id: String, coll_id: String, _headers: AdminHeaders,
} }
#[derive(FromForm)] #[derive(FromForm)]
#[allow(non_snake_case)]
struct OrgIdData { struct OrgIdData {
organizationId: String #[form(field = "organizationId")]
organization_id: String
} }
#[get("/ciphers/organization-details?<data>")] #[get("/ciphers/organization-details?<data..>")]
fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> JsonResult { fn get_org_details(data: Form<OrgIdData>, headers: Headers, conn: DbConn) -> JsonResult {
let ciphers = Cipher::find_by_org(&data.organizationId, &conn); let ciphers = Cipher::find_by_org(&data.organization_id, &conn);
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect(); let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
Ok(Json(json!({ Ok(Json(json!({
@ -643,10 +682,10 @@ struct RelationsData {
Value: usize, Value: usize,
} }
#[post("/ciphers/import-organization?<query>", data = "<data>")] #[post("/ciphers/import-organization?<query..>", data = "<data>")]
fn post_org_import(query: OrgIdData, data: JsonUpcase<ImportData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult { fn post_org_import(query: Form<OrgIdData>, data: JsonUpcase<ImportData>, headers: Headers, conn: DbConn, ws: State<WebSocketUsers>) -> EmptyResult {
let data: ImportData = data.into_inner().data; let data: ImportData = data.into_inner().data;
let org_id = query.organizationId; let org_id = query.into_inner().organization_id;
let org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) { let org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) {
Some(user) => user, Some(user) => user,

View File

@ -1,6 +1,8 @@
use data_encoding::BASE32; use data_encoding::BASE32;
use rocket_contrib::{Json, Value}; use rocket_contrib::json::Json;
use serde_json; use serde_json;
use serde_json::Value;
use db::{ use db::{
models::{TwoFactor, TwoFactorType, User}, models::{TwoFactor, TwoFactorType, User},
@ -12,6 +14,24 @@ use crypto;
use api::{ApiResult, JsonResult, JsonUpcase, NumberOrString, PasswordData}; use api::{ApiResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
use auth::Headers; use auth::Headers;
use rocket::Route;
pub fn routes() -> Vec<Route> {
routes![
get_twofactor,
get_recover,
recover,
disable_twofactor,
disable_twofactor_put,
generate_authenticator,
activate_authenticator,
activate_authenticator_put,
generate_u2f,
activate_u2f,
activate_u2f_put,
]
}
#[get("/two-factor")] #[get("/two-factor")]
fn get_twofactor(headers: Headers, conn: DbConn) -> JsonResult { fn get_twofactor(headers: Headers, conn: DbConn) -> JsonResult {
let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn); let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn);

View File

@ -4,7 +4,8 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use rocket::request::{self, Form, FormItems, FromForm, FromRequest, Request}; use rocket::request::{self, Form, FormItems, FromForm, FromRequest, Request};
use rocket::{Outcome, Route}; use rocket::{Outcome, Route};
use rocket_contrib::{Json, Value}; use rocket_contrib::json::Json;
use serde_json::Value;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
@ -21,9 +22,9 @@ pub fn routes() -> Vec<Route> {
routes![login] routes![login]
} }
#[post("/connect/token", data = "<connect_data>")] #[post("/connect/token", data = "<data>")]
fn login(connect_data: Form<ConnectData>, device_type: DeviceType, conn: DbConn, socket: Option<SocketAddr>) -> JsonResult { fn login(data: Form<ConnectData>, device_type: DeviceType, conn: DbConn, socket: Option<SocketAddr>) -> JsonResult {
let data = connect_data.get(); let data: ConnectData = data.into_inner();
match data.grant_type { match data.grant_type {
GrantType::RefreshToken => _refresh_login(data, device_type, conn), GrantType::RefreshToken => _refresh_login(data, device_type, conn),
@ -31,7 +32,7 @@ fn login(connect_data: Form<ConnectData>, device_type: DeviceType, conn: DbConn,
} }
} }
fn _refresh_login(data: &ConnectData, _device_type: DeviceType, conn: DbConn) -> JsonResult { fn _refresh_login(data: ConnectData, _device_type: DeviceType, conn: DbConn) -> JsonResult {
// Extract token // Extract token
let token = data.get("refresh_token"); let token = data.get("refresh_token");
@ -59,7 +60,7 @@ fn _refresh_login(data: &ConnectData, _device_type: DeviceType, conn: DbConn) ->
} }
} }
fn _password_login(data: &ConnectData, device_type: DeviceType, conn: DbConn, remote: Option<SocketAddr>) -> JsonResult { fn _password_login(data: ConnectData, device_type: DeviceType, conn: DbConn, remote: Option<SocketAddr>) -> JsonResult {
// Get the ip for error reporting // Get the ip for error reporting
let ip = match remote { let ip = match remote {
Some(ip) => ip.ip(), Some(ip) => ip.ip(),
@ -319,11 +320,9 @@ impl<'f> FromForm<'f> for ConnectData {
let mut data = HashMap::new(); let mut data = HashMap::new();
// Insert data into map // Insert data into map
for (key, value) in items { for item in items {
match (key.url_decode(), value.url_decode()) { let (key, value) = item.key_value_decoded();
(Ok(key), Ok(value)) => data.insert(key.to_lowercase(), value), data.insert(key.to_lowercase(), value);
_ => return Err("Error decoding key or value".to_string()),
};
} }
// Validate needed values // Validate needed values

View File

@ -12,11 +12,12 @@ pub use self::notifications::routes as notifications_routes;
pub use self::notifications::{start_notification_server, WebSocketUsers, UpdateType}; pub use self::notifications::{start_notification_server, WebSocketUsers, UpdateType};
use rocket::response::status::BadRequest; use rocket::response::status::BadRequest;
use rocket_contrib::Json; use rocket_contrib::json::Json;
use serde_json::Value;
// Type aliases for API methods results // Type aliases for API methods results
type ApiResult<T> = Result<T, BadRequest<Json>>; type ApiResult<T> = Result<T, BadRequest<Json<Value>>>;
type JsonResult = ApiResult<Json>; type JsonResult = ApiResult<Json<Value>>;
type EmptyResult = ApiResult<()>; type EmptyResult = ApiResult<()>;
use util; use util;

View File

@ -1,5 +1,5 @@
use rocket::Route; use rocket::Route;
use rocket_contrib::Json; use rocket_contrib::json::Json;
use serde_json::Value as JsonValue; use serde_json::Value as JsonValue;
use api::JsonResult; use api::JsonResult;

View File

@ -6,7 +6,8 @@ use rocket::response::{self, NamedFile, Responder};
use rocket::response::content::Content; use rocket::response::content::Content;
use rocket::http::{ContentType, Status}; use rocket::http::{ContentType, Status};
use rocket::Route; use rocket::Route;
use rocket_contrib::{Json, Value}; use rocket_contrib::json::Json;
use serde_json::Value;
use CONFIG; use CONFIG;

View File

@ -40,7 +40,7 @@ pub fn decode_jwt(token: &str) -> Result<JWTClaims, String> {
let validation = jwt::Validation { let validation = jwt::Validation {
leeway: 30, // 30 seconds leeway: 30, // 30 seconds
validate_exp: true, validate_exp: true,
validate_iat: true, validate_iat: false, // IssuedAt is the same as NotBefore
validate_nbf: true, validate_nbf: true,
aud: None, aud: None,
iss: Some(JWT_ISSUER.clone()), iss: Some(JWT_ISSUER.clone()),
@ -197,8 +197,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for OrgHeaders {
Outcome::Success(headers) => { Outcome::Success(headers) => {
// org_id is expected to be the first dynamic param // org_id is expected to be the first dynamic param
match request.get_param::<String>(0) { match request.get_param::<String>(0) {
Err(_) => err_handler!("Error getting the organization id"), Some(Ok(org_id)) => {
Ok(org_id) => {
let conn = match request.guard::<DbConn>() { let conn = match request.guard::<DbConn>() {
Outcome::Success(conn) => conn, Outcome::Success(conn) => conn,
_ => err_handler!("Error getting DB") _ => err_handler!("Error getting DB")
@ -227,7 +226,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for OrgHeaders {
user: headers.user, user: headers.user,
org_user_type: org_user.type_, org_user_type: org_user.type_,
}) })
} },
_ => err_handler!("Error getting the organization id"),
} }
} }
} }

View File

@ -1,4 +1,4 @@
use serde_json::Value as JsonValue; use serde_json::Value;
use super::Cipher; use super::Cipher;
use CONFIG; use CONFIG;
@ -29,7 +29,7 @@ impl Attachment {
format!("{}/{}/{}", CONFIG.attachments_folder, self.cipher_uuid, self.id) format!("{}/{}/{}", CONFIG.attachments_folder, self.cipher_uuid, self.id)
} }
pub fn to_json(&self, host: &str) -> JsonValue { pub fn to_json(&self, host: &str) -> Value {
use util::get_display_size; use util::get_display_size;
let web_path = format!("{}/attachments/{}/{}", host, self.cipher_uuid, self.id); let web_path = format!("{}/attachments/{}/{}", host, self.cipher_uuid, self.id);

View File

@ -1,5 +1,5 @@
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use serde_json::Value as JsonValue; use serde_json::Value;
use uuid::Uuid; use uuid::Uuid;
@ -68,23 +68,23 @@ use db::schema::*;
/// Database methods /// Database methods
impl Cipher { impl Cipher {
pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> JsonValue { pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> Value {
use serde_json; use serde_json;
use util::format_date; use util::format_date;
use super::Attachment; use super::Attachment;
let attachments = Attachment::find_by_cipher(&self.uuid, conn); let attachments = Attachment::find_by_cipher(&self.uuid, conn);
let attachments_json: Vec<JsonValue> = attachments.iter().map(|c| c.to_json(host)).collect(); let attachments_json: Vec<Value> = attachments.iter().map(|c| c.to_json(host)).collect();
let fields_json: JsonValue = if let Some(ref fields) = self.fields { let fields_json: Value = if let Some(ref fields) = self.fields {
serde_json::from_str(fields).unwrap() serde_json::from_str(fields).unwrap()
} else { JsonValue::Null }; } else { Value::Null };
let password_history_json: JsonValue = if let Some(ref password_history) = self.password_history { let password_history_json: Value = if let Some(ref password_history) = self.password_history {
serde_json::from_str(password_history).unwrap() serde_json::from_str(password_history).unwrap()
} else { JsonValue::Null }; } else { Value::Null };
let mut data_json: JsonValue = serde_json::from_str(&self.data).unwrap(); let mut data_json: Value = serde_json::from_str(&self.data).unwrap();
// TODO: ******* Backwards compat start ********** // TODO: ******* Backwards compat start **********
// To remove backwards compatibility, just remove this entire section // To remove backwards compatibility, just remove this entire section

View File

@ -1,4 +1,4 @@
use serde_json::Value as JsonValue; use serde_json::Value;
use uuid::Uuid; use uuid::Uuid;
@ -25,7 +25,7 @@ impl Collection {
} }
} }
pub fn to_json(&self) -> JsonValue { pub fn to_json(&self) -> Value {
json!({ json!({
"Id": self.uuid, "Id": self.uuid,
"OrganizationId": self.org_uuid, "OrganizationId": self.org_uuid,

View File

@ -1,5 +1,5 @@
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use serde_json::Value as JsonValue; use serde_json::Value;
use uuid::Uuid; use uuid::Uuid;
@ -42,7 +42,7 @@ impl Folder {
} }
} }
pub fn to_json(&self) -> JsonValue { pub fn to_json(&self) -> Value {
use util::format_date; use util::format_date;
json!({ json!({

View File

@ -1,4 +1,4 @@
use serde_json::Value as JsonValue; use serde_json::Value;
use uuid::Uuid; use uuid::Uuid;
use super::{User, CollectionUser, Invitation}; use super::{User, CollectionUser, Invitation};
@ -70,7 +70,7 @@ impl Organization {
} }
} }
pub fn to_json(&self) -> JsonValue { pub fn to_json(&self) -> Value {
json!({ json!({
"Id": self.uuid, "Id": self.uuid,
"Name": self.name, "Name": self.name,
@ -181,7 +181,7 @@ impl Organization {
} }
impl UserOrganization { impl UserOrganization {
pub fn to_json(&self, conn: &DbConn) -> JsonValue { pub fn to_json(&self, conn: &DbConn) -> Value {
let org = Organization::find_by_uuid(&self.org_uuid, conn).unwrap(); let org = Organization::find_by_uuid(&self.org_uuid, conn).unwrap();
json!({ json!({
@ -209,7 +209,7 @@ impl UserOrganization {
}) })
} }
pub fn to_json_user_details(&self, conn: &DbConn) -> JsonValue { pub fn to_json_user_details(&self, conn: &DbConn) -> Value {
let user = User::find_by_uuid(&self.user_uuid, conn).unwrap(); let user = User::find_by_uuid(&self.user_uuid, conn).unwrap();
json!({ json!({
@ -226,7 +226,7 @@ impl UserOrganization {
}) })
} }
pub fn to_json_collection_user_details(&self, read_only: bool, conn: &DbConn) -> JsonValue { pub fn to_json_collection_user_details(&self, read_only: bool, conn: &DbConn) -> Value {
let user = User::find_by_uuid(&self.user_uuid, conn).unwrap(); let user = User::find_by_uuid(&self.user_uuid, conn).unwrap();
json!({ json!({
@ -241,7 +241,7 @@ impl UserOrganization {
}) })
} }
pub fn to_json_details(&self, conn: &DbConn) -> JsonValue { pub fn to_json_details(&self, conn: &DbConn) -> Value {
let coll_uuids = if self.access_all { let coll_uuids = if self.access_all {
vec![] // If we have complete access, no need to fill the array vec![] // If we have complete access, no need to fill the array
} else { } else {

View File

@ -1,4 +1,4 @@
use serde_json::Value as JsonValue; use serde_json::Value;
use uuid::Uuid; use uuid::Uuid;
@ -59,7 +59,7 @@ impl TwoFactor {
generated == totp_code generated == totp_code
} }
pub fn to_json(&self) -> JsonValue { pub fn to_json(&self) -> Value {
json!({ json!({
"Enabled": self.enabled, "Enabled": self.enabled,
"Key": "", // This key and value vary "Key": "", // This key and value vary
@ -67,7 +67,7 @@ impl TwoFactor {
}) })
} }
pub fn to_json_list(&self) -> JsonValue { pub fn to_json_list(&self) -> Value {
json!({ json!({
"Enabled": self.enabled, "Enabled": self.enabled,
"Type": self.type_, "Type": self.type_,

View File

@ -1,5 +1,5 @@
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use serde_json::Value as JsonValue; use serde_json::Value;
use uuid::Uuid; use uuid::Uuid;
@ -120,14 +120,14 @@ use super::{Cipher, Folder, Device, UserOrganization, UserOrgType};
/// Database methods /// Database methods
impl User { impl User {
pub fn to_json(&self, conn: &DbConn) -> JsonValue { pub fn to_json(&self, conn: &DbConn) -> Value {
use super::{UserOrganization, UserOrgType, UserOrgStatus, TwoFactor}; use super::{UserOrganization, UserOrgType, UserOrgStatus, TwoFactor};
let mut orgs = UserOrganization::find_by_user(&self.uuid, conn); let mut orgs = UserOrganization::find_by_user(&self.uuid, conn);
if self.is_server_admin() { if self.is_server_admin() {
orgs.push(UserOrganization::new_virtual(self.uuid.clone(), UserOrgType::Owner, UserOrgStatus::Confirmed)); orgs.push(UserOrganization::new_virtual(self.uuid.clone(), UserOrgType::Owner, UserOrgStatus::Confirmed));
} }
let orgs_json: Vec<JsonValue> = orgs.iter().map(|c| c.to_json(&conn)).collect(); let orgs_json: Vec<Value> = orgs.iter().map(|c| c.to_json(&conn)).collect();
let twofactor_enabled = !TwoFactor::find_by_user(&self.uuid, conn).is_empty(); let twofactor_enabled = !TwoFactor::find_by_user(&self.uuid, conn).is_empty();
json!({ json!({

View File

@ -1,7 +1,8 @@
#![feature(plugin, custom_derive, vec_remove_item, try_trait)] #![feature(proc_macro_hygiene, decl_macro, custom_derive, vec_remove_item, try_trait)]
#![plugin(rocket_codegen)]
#![recursion_limit="128"] #![recursion_limit="128"]
#![allow(proc_macro_derive_resolution_fallback)] // TODO: Remove this when diesel update fixes warnings #![allow(proc_macro_derive_resolution_fallback)] // TODO: Remove this when diesel update fixes warnings
#[macro_use]
extern crate rocket; extern crate rocket;
extern crate rocket_contrib; extern crate rocket_contrib;
extern crate reqwest; extern crate reqwest;

View File

@ -23,7 +23,7 @@ macro_rules! err {
#[macro_export] #[macro_export]
macro_rules! err_json { macro_rules! err_json {
($expr:expr) => {{ ($expr:expr) => {{
return Err($crate::rocket::response::status::BadRequest(Some($crate::rocket_contrib::Json($expr)))); return Err($crate::rocket::response::status::BadRequest(Some($crate::rocket_contrib::json::Json($expr))));
}} }}
} }