mirror of
https://github.com/yuzu-emu/liftinstall.git
synced 2024-12-22 19:55:39 +00:00
Tweak Patreon authentication implementation
This commit is contained in:
parent
de4246536e
commit
7392e1ef91
10
build.rs
10
build.rs
|
@ -87,8 +87,8 @@ fn main() {
|
|||
// Copy for the main build
|
||||
copy(&target_config, output_dir.join("bootstrap.toml")).expect("Unable to copy config file");
|
||||
|
||||
let yarn_binary = which::which("yarn")
|
||||
.expect("Failed to find yarn - please go ahead and install it!");
|
||||
let yarn_binary =
|
||||
which::which("yarn").expect("Failed to find yarn - please go ahead and install it!");
|
||||
|
||||
// Build and deploy frontend files
|
||||
Command::new(&yarn_binary)
|
||||
|
@ -100,7 +100,8 @@ fn main() {
|
|||
.arg(ui_dir.to_str().expect("Unable to covert path"))
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait().expect("Unable to install Node.JS dependencies using Yarn");
|
||||
.wait()
|
||||
.expect("Unable to install Node.JS dependencies using Yarn");
|
||||
Command::new(&yarn_binary)
|
||||
.args(&[
|
||||
"--cwd",
|
||||
|
@ -115,5 +116,6 @@ fn main() {
|
|||
])
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait().expect("Unable to build frontend assets using Webpack");
|
||||
.wait()
|
||||
.expect("Unable to build frontend assets using Webpack");
|
||||
}
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
//! frontend/rest/services/authentication.rs
|
||||
//!
|
||||
//! Provides mechanisms to authenticate users using JWT.
|
||||
|
||||
use http::{build_client, build_async_client};
|
||||
|
||||
use hyper::header::{ContentLength, ContentType};
|
||||
use reqwest::header::{USER_AGENT};
|
||||
use futures::{Stream, Future};
|
||||
use jwt::{decode, Validation, Algorithm};
|
||||
|
||||
use frontend::rest::services::{WebService, Request, Response, default_future};
|
||||
use frontend::rest::services::Future as InternalFuture;
|
||||
use logging::LoggingErrors;
|
||||
use url::form_urlencoded;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{Future, Stream};
|
||||
|
||||
use hyper::header::{ContentLength, ContentType};
|
||||
|
||||
use jwt::{decode, Algorithm, Validation};
|
||||
|
||||
use reqwest::header::USER_AGENT;
|
||||
|
||||
use url::form_urlencoded;
|
||||
|
||||
use frontend::rest::services::Future as InternalFuture;
|
||||
use frontend::rest::services::{default_future, Request, Response, WebService};
|
||||
|
||||
use http::{build_async_client, build_client};
|
||||
|
||||
use config::JWTValidation;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Auth {
|
||||
username: String,
|
||||
|
@ -39,15 +49,19 @@ pub struct JWTClaims {
|
|||
}
|
||||
|
||||
/// Calls the given server to obtain a JWT token and returns a Future<String> with the response
|
||||
pub fn authenticate_async(url: String, username: String, token: String)
|
||||
-> Box<dyn futures::Future<Item = String, Error = String>> {
|
||||
|
||||
pub fn authenticate_async(
|
||||
url: String,
|
||||
username: String,
|
||||
token: String,
|
||||
) -> Box<dyn futures::Future<Item = String, Error = String>> {
|
||||
// Build the HTTP client up
|
||||
let client = match build_async_client() {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
return Box::new(futures::future::err("Unable to build async web client".to_string()));
|
||||
},
|
||||
return Box::new(futures::future::err(
|
||||
"Unable to build async web client".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Box::new(client.post(&url)
|
||||
|
@ -77,9 +91,7 @@ pub fn authenticate_async(url: String, username: String, token: String)
|
|||
)
|
||||
}
|
||||
|
||||
pub fn authenticate_sync(url: String, username: String, token: String)
|
||||
-> Result<String, String> {
|
||||
|
||||
pub fn authenticate_sync(url: String, username: String, token: String) -> Result<String, String> {
|
||||
// Build the HTTP client up
|
||||
let client = build_client()?;
|
||||
|
||||
|
@ -95,18 +107,21 @@ pub fn authenticate_sync(url: String, username: String, token: String)
|
|||
})?;
|
||||
|
||||
match response.status() {
|
||||
reqwest::StatusCode::OK =>
|
||||
Ok(response.text()
|
||||
.map_err(|e| {
|
||||
format!("Error while converting the response to text {:?}", e)
|
||||
})?),
|
||||
_ => {
|
||||
Err(format!("Error wrong response code from server {:?}", response.status()))
|
||||
}
|
||||
reqwest::StatusCode::OK => Ok(response
|
||||
.text()
|
||||
.map_err(|e| format!("Error while converting the response to text {:?}", e))?),
|
||||
_ => Err(format!(
|
||||
"Error wrong response code from server {:?}",
|
||||
response.status()
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_token(body: String, pub_key_base64: String, validation: Option<JWTValidation>) -> Result<JWTClaims, String> {
|
||||
pub fn validate_token(
|
||||
body: String,
|
||||
pub_key_base64: String,
|
||||
validation: Option<JWTValidation>,
|
||||
) -> Result<JWTClaims, String> {
|
||||
// Get the public key for this authentication url
|
||||
let pub_key = if pub_key_base64.is_empty() {
|
||||
vec![]
|
||||
|
@ -114,8 +129,11 @@ pub fn validate_token(body: String, pub_key_base64: String, validation: Option<J
|
|||
match base64::decode(&pub_key_base64) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return Err(format!("Configured public key was not empty and did not decode as base64 {:?}", err));
|
||||
},
|
||||
return Err(format!(
|
||||
"Configured public key was not empty and did not decode as base64 {:?}",
|
||||
err
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -124,24 +142,35 @@ pub fn validate_token(body: String, pub_key_base64: String, validation: Option<J
|
|||
Some(v) => {
|
||||
let mut valid = Validation::new(Algorithm::RS256);
|
||||
valid.iss = v.iss;
|
||||
if v.aud.is_some() {
|
||||
valid.set_audience(&v.aud.unwrap());
|
||||
if let &Some(ref v) = &v.aud {
|
||||
valid.set_audience(v);
|
||||
}
|
||||
valid
|
||||
}
|
||||
None => Validation::default()
|
||||
None => Validation::default(),
|
||||
};
|
||||
|
||||
// Verify the JWT token
|
||||
decode::<JWTClaims>(&body, pub_key.as_slice(), &validation)
|
||||
.map(|tok| tok.claims)
|
||||
.map_err(|err| format!("Error while decoding the JWT. error: {:?} jwt: {:?}", err, body))
|
||||
.map_err(|err| {
|
||||
format!(
|
||||
"Error while decoding the JWT. error: {:?} jwt: {:?}",
|
||||
err, body
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle(service: &WebService, _req: Request) -> InternalFuture {
|
||||
let framework = service.framework.read().log_expect("InstallerFramework has been dirtied");
|
||||
let framework = service
|
||||
.framework
|
||||
.read()
|
||||
.log_expect("InstallerFramework has been dirtied");
|
||||
let credentials = framework.database.credentials.clone();
|
||||
let config = framework.config.clone().unwrap();
|
||||
let config = framework
|
||||
.config
|
||||
.clone()
|
||||
.log_expect("No in-memory configuration found");
|
||||
|
||||
// If authentication isn't configured, just return immediately
|
||||
if config.authentication.is_none() {
|
||||
|
@ -152,83 +181,100 @@ pub fn handle(service: &WebService, _req: Request) -> InternalFuture {
|
|||
let write_cred_fw = Arc::clone(&service.framework);
|
||||
|
||||
Box::new(
|
||||
_req.body().concat2().map(move |body| {
|
||||
let req = form_urlencoded::parse(body.as_ref())
|
||||
.into_owned()
|
||||
.collect::<HashMap<String, String>>();
|
||||
_req.body()
|
||||
.concat2()
|
||||
.map(move |body| {
|
||||
let req = form_urlencoded::parse(body.as_ref())
|
||||
.into_owned()
|
||||
.collect::<HashMap<String, String>>();
|
||||
|
||||
// Determine which credentials we should use
|
||||
let (username, token) = {
|
||||
let req_username = req.get("username").unwrap();
|
||||
let req_token = req.get("token").unwrap();
|
||||
// if the user didn't provide credentials, and theres nothing stored in the database, return an early error
|
||||
let req_cred_valid = !req_username.is_empty() && !req_token.is_empty();
|
||||
let stored_cred_valid = !credentials.username.is_empty() && !credentials.token.is_empty();
|
||||
if !req_cred_valid && !stored_cred_valid {
|
||||
info!("No passed in credential and no stored credentials to validate");
|
||||
return default_future(Response::new().with_status(hyper::BadRequest));
|
||||
}
|
||||
if req_cred_valid {
|
||||
(req.get("username").unwrap().clone(), req.get("token").unwrap().clone())
|
||||
} else {
|
||||
(credentials.username.clone(), credentials.token.clone())
|
||||
}
|
||||
};
|
||||
// second copy of the credentials so we can move them into a different closure
|
||||
let (username_clone, token_clone) = (username.clone(), token.clone());
|
||||
// Determine which credentials we should use
|
||||
let (username, token) = {
|
||||
let req_username = req.get("username").log_expect("No username in request");
|
||||
let req_token = req.get("token").log_expect("No token in request");
|
||||
|
||||
let authentication = config.authentication.unwrap();
|
||||
let auth_url = authentication.auth_url.clone();
|
||||
let pub_key_base64 = authentication.pub_key_base64.clone();
|
||||
let validation = authentication.validation.clone();
|
||||
// if the user didn't provide credentials, and theres nothing stored in the
|
||||
// database, return an early error
|
||||
let req_cred_valid = !req_username.is_empty() && !req_token.is_empty();
|
||||
let stored_cred_valid =
|
||||
!credentials.username.is_empty() && !credentials.token.is_empty();
|
||||
|
||||
// call the authentication URL to see if we are authenticated
|
||||
Box::new(authenticate_async(auth_url, username.clone(), token.clone())
|
||||
.map(|body| {
|
||||
validate_token(body, pub_key_base64, validation)
|
||||
})
|
||||
.and_then(|res| res)
|
||||
.map(move |claims| {
|
||||
let out = Auth {
|
||||
username: username_clone,
|
||||
token: token_clone,
|
||||
jwt_token: Some(claims.clone()),
|
||||
};
|
||||
// Convert the json to a string and return the json token
|
||||
match serde_json::to_string(&out) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => {
|
||||
Err(format!("Error while converting the claims to JSON string: {:?}", e))
|
||||
}
|
||||
}
|
||||
})
|
||||
.and_then(|res| res)
|
||||
.map(move |json| {
|
||||
{
|
||||
// Store the validated username and password into the installer database
|
||||
let mut framework = write_cred_fw.write().log_expect("InstallerFramework has been dirtied");
|
||||
framework.database.credentials.username = username;
|
||||
framework.database.credentials.token = token;
|
||||
if !req_cred_valid && !stored_cred_valid {
|
||||
info!("No passed in credential and no stored credentials to validate");
|
||||
return default_future(Response::new().with_status(hyper::BadRequest));
|
||||
}
|
||||
|
||||
// Finally return the JSON with the response
|
||||
info!("successfully verified username and token");
|
||||
Response::new()
|
||||
.with_header(ContentLength(json.len() as u64))
|
||||
.with_header(ContentType::json())
|
||||
.with_status(hyper::StatusCode::Ok)
|
||||
.with_body(json)
|
||||
})
|
||||
.map_err(|err| {
|
||||
Response::new().with_status(hyper::StatusCode::InternalServerError)
|
||||
})
|
||||
.or_else(|err| {
|
||||
// Convert the Err value into an Ok value since the error code from this HTTP request is an Ok(response)
|
||||
Ok(err)
|
||||
})
|
||||
)
|
||||
})
|
||||
// Flatten the internal future into the output response future
|
||||
.flatten()
|
||||
if req_cred_valid {
|
||||
(req_username.clone(), req_token.clone())
|
||||
} else {
|
||||
(credentials.username.clone(), credentials.token.clone())
|
||||
}
|
||||
};
|
||||
|
||||
// second copy of the credentials so we can move them into a different closure
|
||||
let (username_clone, token_clone) = (username.clone(), token.clone());
|
||||
|
||||
let authentication = config
|
||||
.authentication
|
||||
.log_expect("No authentication configuration");
|
||||
let auth_url = authentication.auth_url.clone();
|
||||
let pub_key_base64 = authentication.pub_key_base64.clone();
|
||||
let validation = authentication.validation.clone();
|
||||
|
||||
// call the authentication URL to see if we are authenticated
|
||||
Box::new(
|
||||
authenticate_async(auth_url, username.clone(), token.clone())
|
||||
.map(|body| validate_token(body, pub_key_base64, validation))
|
||||
.and_then(|res| res)
|
||||
.map(move |claims| {
|
||||
let out = Auth {
|
||||
username: username_clone,
|
||||
token: token_clone,
|
||||
jwt_token: Some(claims.clone()),
|
||||
};
|
||||
// Convert the json to a string and return the json token
|
||||
match serde_json::to_string(&out) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => Err(format!(
|
||||
"Error while converting the claims to JSON string: {:?}",
|
||||
e
|
||||
)),
|
||||
}
|
||||
})
|
||||
.and_then(|res| res)
|
||||
.map(move |json| {
|
||||
{
|
||||
// Store the validated username and password into the installer database
|
||||
let mut framework = write_cred_fw
|
||||
.write()
|
||||
.log_expect("InstallerFramework has been dirtied");
|
||||
framework.database.credentials.username = username;
|
||||
framework.database.credentials.token = token;
|
||||
}
|
||||
|
||||
// Finally return the JSON with the response
|
||||
info!("successfully verified username and token");
|
||||
Response::new()
|
||||
.with_header(ContentLength(json.len() as u64))
|
||||
.with_header(ContentType::json())
|
||||
.with_status(hyper::StatusCode::Ok)
|
||||
.with_body(json)
|
||||
})
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"Got an internal error while processing user token: {:?}",
|
||||
err
|
||||
);
|
||||
Response::new().with_status(hyper::StatusCode::InternalServerError)
|
||||
})
|
||||
.or_else(|err| {
|
||||
// Convert the Err value into an Ok value since the error code from
|
||||
// this HTTP request is an Ok(response)
|
||||
Ok(err)
|
||||
}),
|
||||
)
|
||||
})
|
||||
// Flatten the internal future into the output response future
|
||||
.flatten(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
//! frontend/rest/services/browser.rs
|
||||
//!
|
||||
//! Launches the user's web browser on request from the frontend.
|
||||
|
||||
|
||||
use frontend::rest::services::{WebService, Request, Response};
|
||||
use frontend::rest::services::Future as InternalFuture;
|
||||
use futures::{Stream, Future};
|
||||
use url::form_urlencoded;
|
||||
use std::collections::HashMap;
|
||||
use frontend::rest::services::{Request, Response, WebService};
|
||||
use futures::{Future, Stream};
|
||||
use hyper::header::ContentType;
|
||||
use logging::LoggingErrors;
|
||||
use std::collections::HashMap;
|
||||
use url::form_urlencoded;
|
||||
|
||||
pub fn handle(_service: &WebService, _req: Request) -> InternalFuture {
|
||||
Box::new(
|
||||
_req.body().concat2().map(move |body| {
|
||||
Box::new(_req.body().concat2().map(move |body| {
|
||||
let req = form_urlencoded::parse(body.as_ref())
|
||||
.into_owned()
|
||||
.collect::<HashMap<String, String>>();
|
||||
if webbrowser::open( req.get("url").unwrap()).is_ok() {
|
||||
if webbrowser::open(req.get("url").log_expect("No URL to launch")).is_ok() {
|
||||
Response::new()
|
||||
.with_status(hyper::Ok)
|
||||
.with_header(ContentType::json())
|
||||
|
@ -26,4 +28,3 @@ pub fn handle(_service: &WebService, _req: Request) -> InternalFuture {
|
|||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ mod attributes;
|
|||
pub mod authentication;
|
||||
mod browser;
|
||||
mod config;
|
||||
mod default_path;
|
||||
mod dark_mode;
|
||||
mod default_path;
|
||||
mod exit;
|
||||
mod install;
|
||||
mod installation_status;
|
||||
|
|
|
@ -12,11 +12,11 @@ use log::Level;
|
|||
enum CallbackType {
|
||||
SelectInstallDir { callback_name: String },
|
||||
Log { msg: String, kind: String },
|
||||
Test {}
|
||||
Test {},
|
||||
}
|
||||
|
||||
/// Starts the main web UI. Will return when UI is closed.
|
||||
pub fn start_ui(app_name: &str, http_address: &str, is_launcher: bool) {
|
||||
pub fn start_ui(app_name: &str, http_address: &str, _is_launcher: bool) {
|
||||
let size = (1024, 550);
|
||||
|
||||
info!("Spawning web view instance");
|
||||
|
|
40
src/http.rs
40
src/http.rs
|
@ -9,6 +9,7 @@ use std::time::Duration;
|
|||
|
||||
use reqwest::async::Client as AsyncClient;
|
||||
use reqwest::Client;
|
||||
use reqwest::StatusCode;
|
||||
|
||||
/// Asserts that a URL is valid HTTPS, else returns an error.
|
||||
pub fn assert_ssl(url: &str) -> Result<(), String> {
|
||||
|
@ -36,22 +37,39 @@ pub fn build_async_client() -> Result<AsyncClient, String> {
|
|||
}
|
||||
|
||||
/// Streams a file from a HTTP server.
|
||||
pub fn stream_file<F>(url: &str, authorization: Option<String>, mut callback: F) -> Result<(), String>
|
||||
pub fn stream_file<F>(
|
||||
url: &str,
|
||||
authorization: Option<String>,
|
||||
mut callback: F,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
F: FnMut(Vec<u8>, u64) -> (),
|
||||
{
|
||||
assert_ssl(url)?;
|
||||
|
||||
let mut client = if authorization.is_some() {
|
||||
build_client()?.get(url)
|
||||
.header("Authorization", format!("Bearer {}", authorization.unwrap()))
|
||||
.send()
|
||||
.map_err(|x| format!("Failed to GET resource: {:?}", x))?
|
||||
} else {
|
||||
build_client()?.get(url)
|
||||
.send()
|
||||
.map_err(|x| format!("Failed to GET resource: {:?}", x))?
|
||||
};
|
||||
let mut client = build_client()?.get(url);
|
||||
|
||||
if let Some(auth) = authorization {
|
||||
client = client.header("Authorization", format!("Bearer {}", auth));
|
||||
}
|
||||
|
||||
let mut client = client
|
||||
.send()
|
||||
.map_err(|x| format!("Failed to GET resource: {:?}", x))?;
|
||||
|
||||
match client.status() {
|
||||
StatusCode::OK => {}
|
||||
StatusCode::TOO_MANY_REQUESTS => {
|
||||
return Err(
|
||||
"Your token has exceeded the number of daily allowable IP addresses. \
|
||||
Please wait 24 hours and try again."
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
x => {
|
||||
return Err(format!("Bad status code: {:?}.", x));
|
||||
}
|
||||
}
|
||||
|
||||
let size = match client.headers().get(CONTENT_LENGTH) {
|
||||
Some(ref v) => v
|
||||
|
|
|
@ -77,7 +77,10 @@ impl InstallationDatabase {
|
|||
InstallationDatabase {
|
||||
packages: Vec::new(),
|
||||
shortcuts: Vec::new(),
|
||||
credentials: Credentials{username: String::new(), token: String::new()},
|
||||
credentials: Credentials {
|
||||
username: String::new(),
|
||||
token: String::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +130,8 @@ macro_rules! declare_messenger_callback {
|
|||
}
|
||||
}
|
||||
TaskMessage::AuthorizationRequired(msg) => {
|
||||
if let Err(v) = $target.send(InstallMessage::AuthorizationRequired(msg.to_string())) {
|
||||
if let Err(v) = $target.send(InstallMessage::AuthorizationRequired(msg.to_string()))
|
||||
{
|
||||
error!("Failed to submit queue message: {:?}", v);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,9 +38,9 @@ extern crate chrono;
|
|||
|
||||
extern crate clap;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
#[cfg(windows)]
|
||||
extern crate widestring;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
extern crate slug;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! github/mod.rs
|
||||
//! patreon.rs
|
||||
//!
|
||||
//! Contains the Github API implementation of a release source.
|
||||
//! Contains the yuzu-emu core API implementation of a release source.
|
||||
|
||||
use sources::types::*;
|
||||
use http::build_client;
|
||||
use reqwest::header::USER_AGENT;
|
||||
use reqwest::StatusCode;
|
||||
use sources::types::*;
|
||||
|
||||
pub struct PatreonReleases {}
|
||||
|
||||
|
@ -44,9 +44,7 @@ impl ReleaseSource for PatreonReleases {
|
|||
match response.status() {
|
||||
StatusCode::OK => {}
|
||||
StatusCode::FORBIDDEN => {
|
||||
return Err(
|
||||
"You are not eligible to download this release".to_string(),
|
||||
);
|
||||
return Err("You are not eligible to download this release".to_string());
|
||||
}
|
||||
_ => {
|
||||
return Err(format!("Bad status code: {:?}.", response.status()));
|
||||
|
@ -77,18 +75,14 @@ impl ReleaseSource for PatreonReleases {
|
|||
let string = match file["name"].as_str() {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(
|
||||
"JSON payload missing information about release name".to_string()
|
||||
);
|
||||
return Err("JSON payload missing information about release name".to_string());
|
||||
}
|
||||
};
|
||||
|
||||
let url = match file["url"].as_str() {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(
|
||||
"JSON payload missing information about release URL".to_string()
|
||||
);
|
||||
return Err("JSON payload missing information about release URL".to_string());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
//! Validates that users have correct authorization to download packages.
|
||||
|
||||
use frontend::rest::services::authentication;
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use tasks::{Task, TaskDependency, TaskMessage, TaskOrdering, TaskParamType};
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use frontend::rest::services::authentication;
|
||||
use futures::{Stream, Future};
|
||||
|
||||
use tasks::resolver::ResolvePackageTask;
|
||||
use tasks::{Task, TaskDependency, TaskMessage, TaskOrdering, TaskParamType};
|
||||
|
||||
pub struct CheckAuthorizationTask {
|
||||
pub name: String,
|
||||
|
@ -15,14 +18,14 @@ impl Task for CheckAuthorizationTask {
|
|||
&mut self,
|
||||
mut input: Vec<TaskParamType>,
|
||||
context: &mut InstallerFramework,
|
||||
messenger: &dyn Fn(&TaskMessage),
|
||||
_messenger: &dyn Fn(&TaskMessage),
|
||||
) -> Result<TaskParamType, String> {
|
||||
|
||||
assert_eq!(input.len(), 1);
|
||||
|
||||
let params = input.pop().log_expect("Should have input from resolver!");
|
||||
let (version, file) = match params {
|
||||
TaskParamType::File(v, f) => { Ok((v, f)) },
|
||||
_ => { Err("Unexpected TaskParamType in CheckAuthorization: {:?}") }
|
||||
TaskParamType::File(v, f) => Ok((v, f)),
|
||||
_ => Err("Unexpected TaskParamType in CheckAuthorization: {:?}"),
|
||||
}?;
|
||||
|
||||
if !file.requires_authorization {
|
||||
|
@ -31,27 +34,41 @@ impl Task for CheckAuthorizationTask {
|
|||
|
||||
let username = context.database.credentials.username.clone();
|
||||
let token = context.database.credentials.token.clone();
|
||||
let authentication = context.config.clone().unwrap().authentication.unwrap();
|
||||
let authentication = context
|
||||
.config
|
||||
.clone()
|
||||
.log_expect("In-memory configuration doesn't exist")
|
||||
.authentication
|
||||
.log_expect("No authentication configuration exists while checking authorization");
|
||||
|
||||
let auth_url = authentication.auth_url.clone();
|
||||
let pub_key_base64 = authentication.pub_key_base64.clone();
|
||||
let validation = authentication.validation.clone();
|
||||
|
||||
// Authorizaion is required for this package so post the username and token and get a jwt_token response
|
||||
let jwt_token = match authentication::authenticate_sync(auth_url, username, token) {
|
||||
Ok(jwt) => jwt,
|
||||
Err(_) => return Ok(TaskParamType::Authentication(version, file, None))
|
||||
Err(_) => return Ok(TaskParamType::Authentication(version, file, None)),
|
||||
};
|
||||
let claims = match authentication::validate_token(jwt_token.clone(), pub_key_base64, validation) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return Ok(TaskParamType::Authentication(version, file, None))
|
||||
};
|
||||
// Validate that they are authorized
|
||||
let authorized =
|
||||
claims.roles.contains(&"vip".to_string()) || (claims.channels.contains(&"early-access".to_string()));
|
||||
|
||||
if !authorized {
|
||||
let claims =
|
||||
match authentication::validate_token(jwt_token.clone(), pub_key_base64, validation) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return Ok(TaskParamType::Authentication(version, file, None)),
|
||||
};
|
||||
|
||||
// Validate that they are authorized
|
||||
if !claims.roles.contains(&"vip".to_string())
|
||||
&& !claims.channels.contains(&"early-access".to_string())
|
||||
{
|
||||
return Ok(TaskParamType::Authentication(version, file, None));
|
||||
}
|
||||
Ok(TaskParamType::Authentication(version, file, Some(jwt_token)))
|
||||
|
||||
Ok(TaskParamType::Authentication(
|
||||
version,
|
||||
file,
|
||||
Some(jwt_token),
|
||||
))
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
|
@ -66,4 +83,4 @@ impl Task for CheckAuthorizationTask {
|
|||
fn name(&self) -> String {
|
||||
format!("CheckAuthorizationTask (for {:?})", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ use installer::InstallerFramework;
|
|||
use tasks::check_authorization::CheckAuthorizationTask;
|
||||
use tasks::{Task, TaskDependency, TaskMessage, TaskOrdering, TaskParamType};
|
||||
|
||||
use tasks::resolver::ResolvePackageTask;
|
||||
|
||||
use http::stream_file;
|
||||
|
||||
use number_prefix::{NumberPrefix, Prefixed, Standalone};
|
||||
|
@ -32,7 +30,7 @@ impl Task for DownloadPackageTask {
|
|||
_ => return Err("Unexpected param type to download package".to_string()),
|
||||
};
|
||||
|
||||
// TODO: move this back below checking for latest version after testing is done
|
||||
// TODO: move this back below checking for latest version after testing is done
|
||||
if file.requires_authorization && auth.is_none() {
|
||||
info!("Authorization required to update this package!");
|
||||
messenger(&TaskMessage::AuthorizationRequired("AuthorizationRequired"));
|
||||
|
@ -94,12 +92,12 @@ impl Task for DownloadPackageTask {
|
|||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(CheckAuthorizationTask {
|
||||
name: self.name.clone(),
|
||||
}),
|
||||
)]
|
||||
vec![TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(CheckAuthorizationTask {
|
||||
name: self.name.clone(),
|
||||
}),
|
||||
)]
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
|
|
Loading…
Reference in a new issue