Add RC account info
This commit is contained in:
parent
ce31f89fb9
commit
77ef47dce4
6 changed files with 205 additions and 4 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "libfj"
|
name = "libfj"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "An unofficial collection of APIs used in FreeJam games and mods"
|
description = "An unofficial collection of APIs used in FreeJam games and mods"
|
||||||
|
@ -35,8 +35,10 @@ genmesh = {version = "^0.6", optional = true}
|
||||||
tokio = { version = "1.4.0", features = ["macros"]}
|
tokio = { version = "1.4.0", features = ["macros"]}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
all = ["simple", "robocraft", "cardlife", "techblox", "convert"]
|
||||||
|
default = ["all"]
|
||||||
simple = ["ureq"]
|
simple = ["ureq"]
|
||||||
robocraft = ["reqwest"]
|
robocraft = ["reqwest", "ureq"]
|
||||||
cardlife = ["reqwest"]
|
cardlife = ["reqwest"]
|
||||||
techblox = ["chrono", "highhash", "half", "libfj_parsable_macro_derive"]
|
techblox = ["chrono", "highhash", "half", "libfj_parsable_macro_derive"]
|
||||||
convert = ["obj", "genmesh"]
|
convert = ["obj", "genmesh"]
|
||||||
|
|
163
src/robocraft/account.rs
Normal file
163
src/robocraft/account.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ureq::{Agent, Error};
|
||||||
|
use serde_json::{to_string, from_slice};
|
||||||
|
|
||||||
|
use crate::robocraft::ITokenProvider;
|
||||||
|
|
||||||
|
/// Token provider for an existing Robocraft account.
|
||||||
|
///
|
||||||
|
/// Steam accounts are not supported.
|
||||||
|
pub struct AuthenticatedTokenProvider {
|
||||||
|
/// The account's username
|
||||||
|
pub username: String,
|
||||||
|
/// The account's password
|
||||||
|
pub password: String,
|
||||||
|
/// Ureq HTTP client
|
||||||
|
client: Agent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthenticatedTokenProvider {
|
||||||
|
pub fn with_email(email: &str, password: &str) -> Result<Self, Error> {
|
||||||
|
let client = Agent::new();
|
||||||
|
let payload = AuthenticationEmailPayload {
|
||||||
|
email_address: email.to_string(),
|
||||||
|
password: password.to_string(),
|
||||||
|
};
|
||||||
|
let response = client.post("https://account.freejamgames.com/api/authenticate/email/web")
|
||||||
|
.set("Content-Type", "application/json")
|
||||||
|
.send_string(&to_string(&payload).unwrap())?;
|
||||||
|
let json_res = response.into_json::<AuthenticationResponseInfo>()?;
|
||||||
|
Ok(Self {
|
||||||
|
username: json_res.decode_jwt_data().display_name,
|
||||||
|
password: password.to_string(),
|
||||||
|
client,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_username(username: &str, password: &str) -> Result<Self, Error> {
|
||||||
|
let new_obj = Self {
|
||||||
|
username: username.to_string(),
|
||||||
|
password: password.to_string(),
|
||||||
|
client: Agent::new(),
|
||||||
|
};
|
||||||
|
new_obj.do_auth()?;
|
||||||
|
Ok(new_obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_auth(&self) -> Result<AuthenticationResponseInfo, Error> {
|
||||||
|
let payload = AuthenticationUsernamePayload {
|
||||||
|
username: self.username.clone(),
|
||||||
|
password: self.password.clone(),
|
||||||
|
};
|
||||||
|
let response = self.client.post("https://account.freejamgames.com/api/authenticate/displayname/web")
|
||||||
|
.set("Content-Type", "application/json")
|
||||||
|
.send_string(&to_string(&payload).unwrap())?;
|
||||||
|
let json_res = response.into_json::<AuthenticationResponseInfo>()?;
|
||||||
|
Ok(json_res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_account_info(&self) -> Result<AccountInfo, Error> {
|
||||||
|
let json_res = self.do_auth()?;
|
||||||
|
Ok(json_res.decode_jwt_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ITokenProvider for AuthenticatedTokenProvider {
|
||||||
|
fn token(&self) -> Result<String, ()> {
|
||||||
|
let json_res = self.do_auth().map_err(|_|())?;
|
||||||
|
Ok(json_res.token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub(crate) struct AuthenticationEmailPayload {
|
||||||
|
#[serde(rename = "EmailAddress")]
|
||||||
|
pub email_address: String,
|
||||||
|
#[serde(rename = "Password")]
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub(crate) struct AuthenticationUsernamePayload {
|
||||||
|
#[serde(rename = "DisplayName")]
|
||||||
|
pub username: String,
|
||||||
|
#[serde(rename = "Password")]
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub(crate) struct AuthenticationResponseInfo {
|
||||||
|
#[serde(rename = "Token")]
|
||||||
|
pub token: String,
|
||||||
|
#[serde(rename = "RefreshToken")]
|
||||||
|
pub refresh_token: String,
|
||||||
|
#[serde(rename = "RefreshTokenExpiry")]
|
||||||
|
pub refresh_token_expiry: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthenticationResponseInfo {
|
||||||
|
pub fn decode_jwt_data(&self) -> AccountInfo {
|
||||||
|
// Refer to https://jwt.io/
|
||||||
|
// header is before dot, signature is after dot.
|
||||||
|
// data is sandwiched in the middle, and it's all we care about
|
||||||
|
let data = self.token.split(".").collect::<Vec<&str>>()[1];
|
||||||
|
let data_vec = base64::decode(data).unwrap();
|
||||||
|
from_slice::<AccountInfo>(&data_vec).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Robocraft account information.
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct AccountInfo {
|
||||||
|
/// User's public ID
|
||||||
|
#[serde(rename = "PublicId")]
|
||||||
|
pub public_id: String,
|
||||||
|
/// Account display name
|
||||||
|
#[serde(rename = "DisplayName")]
|
||||||
|
pub display_name: String,
|
||||||
|
/// Account GUID, or display name for older accounts
|
||||||
|
#[serde(rename = "RobocraftName")]
|
||||||
|
pub robocraft_name: String,
|
||||||
|
/// ??? is confirmed?
|
||||||
|
#[serde(rename = "Confirmed")]
|
||||||
|
pub confirmed: bool,
|
||||||
|
/// Freejam support code
|
||||||
|
#[serde(rename = "SupportCode")]
|
||||||
|
pub support_code: String,
|
||||||
|
/// User's email address
|
||||||
|
#[serde(rename = "EmailAddress")]
|
||||||
|
pub email_address: String,
|
||||||
|
/// Email address is verified?
|
||||||
|
#[serde(rename = "EmailVerified")]
|
||||||
|
pub email_verified: bool,
|
||||||
|
/// Account creation date
|
||||||
|
#[serde(rename = "CreatedDate")]
|
||||||
|
pub created_date: String,
|
||||||
|
/// Owned products (?)
|
||||||
|
#[serde(rename = "Products")]
|
||||||
|
pub products: Vec<String>,
|
||||||
|
/// Account flags
|
||||||
|
#[serde(rename = "Flags")]
|
||||||
|
pub flags: Vec<String>,
|
||||||
|
/// Account has a password?
|
||||||
|
#[serde(rename = "HasPassword")]
|
||||||
|
pub has_password: bool,
|
||||||
|
/// Mailing lists that the account is signed up for
|
||||||
|
#[serde(rename = "MailingLists")]
|
||||||
|
pub mailing_lists: Vec<String>,
|
||||||
|
/// Is Steam account? (always false)
|
||||||
|
#[serde(rename = "HasSteam")]
|
||||||
|
pub has_steam: bool,
|
||||||
|
/// iss (?)
|
||||||
|
#[serde(rename = "iss")]
|
||||||
|
pub iss: String,
|
||||||
|
/// sub (?)
|
||||||
|
#[serde(rename = "sub")]
|
||||||
|
pub sub: String,
|
||||||
|
/// Token created at (unix time) (?)
|
||||||
|
#[serde(rename = "iat")]
|
||||||
|
pub iat: u64,
|
||||||
|
/// Token expiry (unix time) (?)
|
||||||
|
#[serde(rename = "exp")]
|
||||||
|
pub exp: u64,
|
||||||
|
}
|
|
@ -16,5 +16,8 @@ pub use self::cubes::{Cube, Cubes};
|
||||||
mod auth;
|
mod auth;
|
||||||
pub use self::auth::{ITokenProvider, DefaultTokenProvider};
|
pub use self::auth::{ITokenProvider, DefaultTokenProvider};
|
||||||
|
|
||||||
|
mod account;
|
||||||
|
pub use self::account::{AuthenticatedTokenProvider, AccountInfo};
|
||||||
|
|
||||||
/// Token defined in a javascript file from Freejam which never expires
|
/// Token defined in a javascript file from Freejam which never expires
|
||||||
pub const DEFAULT_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJQdWJsaWNJZCI6IjEyMyIsIkRpc3BsYXlOYW1lIjoiVGVzdCIsIlJvYm9jcmFmdE5hbWUiOiJGYWtlQ1JGVXNlciIsIkZsYWdzIjpbXSwiaXNzIjoiRnJlZWphbSIsInN1YiI6IldlYiIsImlhdCI6MTU0NTIyMzczMiwiZXhwIjoyNTQ1MjIzNzkyfQ.ralLmxdMK9rVKPZxGng8luRIdbTflJ4YMJcd25dKlqg";
|
pub const DEFAULT_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJQdWJsaWNJZCI6IjEyMyIsIkRpc3BsYXlOYW1lIjoiVGVzdCIsIlJvYm9jcmFmdE5hbWUiOiJGYWtlQ1JGVXNlciIsIkZsYWdzIjpbXSwiaXNzIjoiRnJlZWphbSIsInN1YiI6IldlYiIsImlhdCI6MTU0NTIyMzczMiwiZXhwIjoyNTQ1MjIzNzkyfQ.ralLmxdMK9rVKPZxGng8luRIdbTflJ4YMJcd25dKlqg";
|
||||||
|
|
3
test.sh
3
test.sh
|
@ -1,5 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
RUST_BACKTRACE=1 cargo test --all-features -- --nocapture
|
# RUST_BACKTRACE=1 cargo test --all-features -- --nocapture
|
||||||
# RUST_BACKTRACE=1 cargo test --release --all-features -- --nocapture
|
# RUST_BACKTRACE=1 cargo test --release --all-features -- --nocapture
|
||||||
# RUST_BACKTRACE=1 cargo test --features techblox -- --nocapture
|
# RUST_BACKTRACE=1 cargo test --features techblox -- --nocapture
|
||||||
|
RUST_BACKTRACE=1 cargo test --features robocraft -- --nocapture
|
||||||
exit $?
|
exit $?
|
||||||
|
|
32
tests/robocraft_auth.rs
Normal file
32
tests/robocraft_auth.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#[cfg(feature = "robocraft")]
|
||||||
|
use libfj::robocraft;
|
||||||
|
#[cfg(feature = "robocraft")]
|
||||||
|
use libfj::robocraft::ITokenProvider;
|
||||||
|
|
||||||
|
#[cfg(feature = "robocraft")]
|
||||||
|
#[test]
|
||||||
|
fn robocraft_auth_login() -> Result<(), ()> {
|
||||||
|
let token_maybe = robocraft::AuthenticatedTokenProvider::with_email("melon.spoik@gmail.com", "P4$$w0rd");
|
||||||
|
assert!(token_maybe.is_ok());
|
||||||
|
let token_maybe = robocraft::AuthenticatedTokenProvider::with_username("FJAPIC00L", "P4$$w0rd");
|
||||||
|
assert!(token_maybe.is_ok());
|
||||||
|
let token_p = token_maybe.unwrap();
|
||||||
|
let raw_token_maybe = token_p.token();
|
||||||
|
assert!(raw_token_maybe.is_ok());
|
||||||
|
println!("Token: {}", raw_token_maybe.unwrap());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "robocraft")]
|
||||||
|
#[test]
|
||||||
|
fn robocraft_account() -> Result<(), ()> {
|
||||||
|
let token_maybe = robocraft::AuthenticatedTokenProvider::with_username("FJAPIC00L", "P4$$w0rd");
|
||||||
|
assert!(token_maybe.is_ok());
|
||||||
|
let token_provider = token_maybe.unwrap();
|
||||||
|
let account_maybe = token_provider.get_account_info();
|
||||||
|
assert!(account_maybe.is_ok());
|
||||||
|
let account = account_maybe.unwrap();
|
||||||
|
assert_eq!(account.display_name, "FJAPIC00L");
|
||||||
|
assert_eq!(account.created_date, "2019-01-18T14:48:09");
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -78,7 +78,7 @@ async fn robocraft_factory_custom_query() -> Result<(), ()> {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn robocraft_factory_player_query() -> Result<(), ()> {
|
async fn robocraft_factory_player_query() -> Result<(), ()> {
|
||||||
let result = builder()
|
let result = builder()
|
||||||
.text("MilanZhi".to_string()) // there is a featured robot by this user, so this should never fail
|
.text("Spacecam".to_string()) // there is a featured robot by this user, so this should never fail
|
||||||
.text_search_type(robocraft::FactoryTextSearchType::Player)
|
.text_search_type(robocraft::FactoryTextSearchType::Player)
|
||||||
.items_per_page(10)
|
.items_per_page(10)
|
||||||
.send().await;
|
.send().await;
|
||||||
|
|
Loading…
Add table
Reference in a new issue