use std::collections::HashMap; use axum::{ http::{header, HeaderValue, Method, StatusCode}, Router, }; use axum_test::TestServer; use chrono::{DateTime, SubsecRound, TimeDelta, Utc}; use laboratory::{expect, SpecContext}; use mongodb::bson::Document; use serde_json::{Map, Value}; use sql_builder::SqlBuilder; use sqlx; use tokio::runtime::Runtime; use sylvia_iot_auth::{ models::client::{Client, QueryCond}, routes, }; use sylvia_iot_corelib::{err, role::Role}; use crate::routes::v1::libs::get_token_client_id; use super::{ super::{ super::libs::{create_client, create_user}, libs::{ get_token, test_get_list_invalid_param, test_invalid_perm, test_invalid_token, ApiError, }, TestState, STATE, }, request, response, }; const NO_ID_ERR_STR: &'static str = "get no client with ID"; pub fn before_all_fn(state: &mut HashMap<&'static str, TestState>) { let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let model = state.routes_state.as_ref().unwrap().model.as_ref(); runtime.block_on(async { let now = Utc::now(); let mut roles = HashMap::::new(); roles.insert(Role::ADMIN.to_string(), true); if let Err(e) = model.user().add(&create_user("admin", now, roles)).await { println!("add user admin error: {}", e); } let mut roles = HashMap::::new(); roles.insert(Role::MANAGER.to_string(), true); if let Err(e) = model.user().add(&create_user("manager", now, roles)).await { println!("add user manager error: {}", e); } let mut roles = HashMap::::new(); roles.insert(Role::SERVICE.to_string(), true); if let Err(e) = model.user().add(&create_user("service", now, roles)).await { println!("add user manager error: {}", e); } let mut roles = HashMap::::new(); roles.insert(Role::DEV.to_string(), true); if let Err(e) = model.user().add(&create_user("public", now, roles)).await { println!("add user public error: {}", e); } let roles = HashMap::::new(); if let Err(e) = model.user().add(&create_user("user", now, roles)).await { println!("add user user error: {}", e); } }) } pub fn after_all_fn(state: &mut HashMap<&'static str, TestState>) -> () { const USER_NAME: &'static str = "user"; let state = state.get_mut(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); if let Some(model) = state.mongodb.as_ref() { runtime.block_on(async { struct Doc; let conn = model.get_connection(); let _ = conn .collection::(USER_NAME) .delete_many(Document::new()) .await; }); } if let Some(model) = state.sqlite.as_ref() { runtime.block_on(async { let conn = model.get_connection(); let sql = SqlBuilder::delete_from(USER_NAME).sql().unwrap(); let _ = sqlx::query(sql.as_str()).execute(conn).await; }); } } pub fn before_each_fn(state: &mut HashMap<&'static str, TestState>) { let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let model = state.routes_state.as_ref().unwrap().model.as_ref(); runtime.block_on(async { let client = create_client("public", "public", None); if let Err(e) = model.client().add(&client).await { println!("add client public error: {}", e); } }) } pub fn after_each_fn(state: &mut HashMap<&'static str, TestState>) -> () { const CLIENT_NAME: &'static str = "client"; let state = state.get_mut(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); if let Some(model) = state.mongodb.as_ref() { runtime.block_on(async { struct Doc; let conn = model.get_connection(); let _ = conn .collection::(CLIENT_NAME) .delete_many(Document::new()) .await; }); } if let Some(model) = state.sqlite.as_ref() { runtime.block_on(async { let conn = model.get_connection(); let sql = SqlBuilder::delete_from(CLIENT_NAME).sql().unwrap(); let _ = sqlx::query(sql.as_str()).execute(conn).await; }); } } pub fn post(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let user_id = "admin"; let token = get_token(runtime, routes_state, user_id)?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![], scopes: vec![], user_id: None, name: "".to_string(), image: None, }, credentials: None, }; test_post(runtime, &routes_state, token.as_str(), user_id, ¶m, "")?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![ "http://uri".to_string(), crate::TEST_REDIRECT_URI.to_string(), crate::TEST_REDIRECT_URI.to_string(), ], scopes: vec![ "scope2".to_string(), "scope1".to_string(), "scope1".to_string(), ], user_id: Some("user".to_string()), name: "name".to_string(), image: Some("image".to_string()), }, credentials: Some(true), }; test_post(runtime, &routes_state, token.as_str(), user_id, ¶m, "")?; let user_id = "public"; let token = get_token(runtime, routes_state, user_id)?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![], scopes: vec![], user_id: None, name: "".to_string(), image: None, }, credentials: Some(false), }; test_post(runtime, &routes_state, token.as_str(), user_id, ¶m, "")?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![ "http://uri".to_string(), crate::TEST_REDIRECT_URI.to_string(), crate::TEST_REDIRECT_URI.to_string(), ], scopes: vec![ "scope2".to_string(), "scope1".to_string(), "scope1".to_string(), ], user_id: Some("user".to_string()), name: "name".to_string(), image: Some("image".to_string()), }, credentials: Some(true), }; test_post(runtime, &routes_state, token.as_str(), user_id, ¶m, "") } pub fn post_dup(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let user_id = "admin"; let token = get_token(runtime, routes_state, user_id)?; let app = Router::new().merge(routes::new_service(routes_state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![ "http://r1".to_string(), "http://r3".to_string(), "http://r1".to_string(), "http://r2".to_string(), ], scopes: vec![ "s1".to_string(), "s3".to_string(), "s1".to_string(), "s4".to_string(), "s2".to_string(), ], user_id: None, name: "".to_string(), image: None, }, credentials: None, }; let req = server .post("/auth/api/v1/client") .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ) .json(¶m); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::OK)?; let body: response::PostClient = resp.json(); expect(body.data.client_id.len() > 0).to_equal(true)?; let client_info = match runtime.block_on(async { let cond = QueryCond { client_id: Some(body.data.client_id.as_str()), ..Default::default() }; routes_state.model.client().get(&cond).await }) { Err(e) => return Err(format!("get client model error: {}", e)), Ok(info) => match info { None => return Err("add client then get none client".to_string()), Some(info) => info, }, }; expect(client_info.redirect_uris.len()).to_equal(3)?; expect(client_info.redirect_uris[0].as_str()).to_equal("http://r1")?; expect(client_info.redirect_uris[1].as_str()).to_equal("http://r2")?; expect(client_info.redirect_uris[2].as_str()).to_equal("http://r3")?; expect(client_info.scopes.len()).to_equal(4)?; expect(client_info.scopes[0].as_str()).to_equal("s1")?; expect(client_info.scopes[1].as_str()).to_equal("s2")?; expect(client_info.scopes[2].as_str()).to_equal("s3")?; expect(client_info.scopes[3].as_str()).to_equal("s4") } pub fn post_not_exist_user(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let user_id = "admin"; let token = get_token(runtime, routes_state, user_id)?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![], scopes: vec![], user_id: Some("test".to_string()), name: "".to_string(), image: None, }, credentials: None, }; test_post( runtime, &routes_state, token.as_str(), user_id, ¶m, "err_auth_user_not_exist", ) } pub fn post_invalid_param(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let user_id = "admin"; let token = get_token(runtime, routes_state, user_id)?; test_post_invalid_param(runtime, &routes_state, token.as_str(), None)?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec!["".to_string()], scopes: vec![], user_id: Some("admin".to_string()), name: "".to_string(), image: None, }, credentials: None, }; test_post_invalid_param(runtime, &routes_state, token.as_str(), Some(¶m))?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![":://".to_string()], scopes: vec![], user_id: Some("admin".to_string()), name: "".to_string(), image: None, }, credentials: None, }; test_post_invalid_param(runtime, &routes_state, token.as_str(), Some(¶m))?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![], scopes: vec!["".to_string()], user_id: Some("admin".to_string()), name: "".to_string(), image: None, }, credentials: None, }; test_post_invalid_param(runtime, &routes_state, token.as_str(), Some(¶m))?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![], scopes: vec!["scope..abc".to_string()], user_id: Some("admin".to_string()), name: "".to_string(), image: None, }, credentials: None, }; test_post_invalid_param(runtime, &routes_state, token.as_str(), Some(¶m))?; let param = request::PostClient { data: request::PostClientData { redirect_uris: vec![], scopes: vec![], user_id: Some("".to_string()), name: "".to_string(), image: None, }, credentials: None, }; test_post_invalid_param(runtime, &routes_state, token.as_str(), Some(¶m)) } pub fn post_invalid_token(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); test_invalid_token(runtime, &routes_state, Method::POST, "/auth/api/v1/client") } pub fn post_invalid_perm(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let method = Method::POST; let uri = "/auth/api/v1/client"; let token = get_token(runtime, routes_state, "manager")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "service")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "user")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method, uri) } pub fn get_count(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let data_size = count_list_dataset(runtime, &routes_state)?; let admin_token = get_token(runtime, routes_state, "admin")?; let public_token = get_token(runtime, routes_state, "public")?; test_get_count( runtime, &routes_state, admin_token.as_str(), None, data_size.0, )?; test_get_count(runtime, &routes_state, public_token.as_str(), None, 1)?; let param = request::GetClientCount { user: Some("user".to_string()), }; test_get_count( runtime, &routes_state, admin_token.as_str(), Some(¶m), data_size.1, )?; test_get_count(runtime, &routes_state, public_token.as_str(), None, 1) } pub fn get_count_invalid_token(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); test_invalid_token( runtime, &routes_state, Method::GET, "/auth/api/v1/client/count", ) } pub fn get_count_invalid_perm(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let method = Method::GET; let uri = "/auth/api/v1/client/count"; let token = get_token(runtime, routes_state, "manager")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "service")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "user")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method, uri) } pub fn get_list(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let data_size = count_list_dataset(runtime, &routes_state)?; let admin_token = get_token(runtime, routes_state, "admin")?; let public_token = get_token(runtime, routes_state, "public")?; test_get_list( runtime, &routes_state, admin_token.as_str(), true, None, data_size.0, )?; test_get_list( runtime, &routes_state, public_token.as_str(), false, None, 1, )?; let param = request::GetClientList { user: Some("user".to_string()), ..Default::default() }; test_get_list( runtime, &routes_state, admin_token.as_str(), true, Some(¶m), data_size.1, )?; test_get_list( runtime, &routes_state, public_token.as_str(), false, Some(¶m), 1, ) } pub fn get_list_sort(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); count_list_dataset(runtime, &routes_state)?; let admin_token = get_token(runtime, routes_state, "admin")?; let mut param = request::GetClientList { ..Default::default() }; test_get_list_sort( runtime, &routes_state, admin_token.as_str(), &mut param, &[ "client_admin1", "client_admin2", "client_user2", "client_user1", "public", ], )?; param.sort_vec = Some(vec![("name", true)]); test_get_list_sort( runtime, &routes_state, admin_token.as_str(), &mut param, &[ "client_admin1", "client_admin2", "client_user2", "client_user1", "public", ], )?; param.sort_vec = Some(vec![("name", false)]); test_get_list_sort( runtime, &routes_state, admin_token.as_str(), &mut param, &[ "public", "client_user1", "client_user2", "client_admin2", "client_admin1", ], )?; param.sort_vec = Some(vec![("created", true)]); test_get_list_sort( runtime, &routes_state, admin_token.as_str(), &mut param, &[ "public", "client_admin1", "client_admin2", "client_user1", "client_user2", ], )?; param.sort_vec = Some(vec![("created", false)]); test_get_list_sort( runtime, &routes_state, admin_token.as_str(), &mut param, &[ "client_user2", "client_user1", "client_admin2", "client_admin1", "public", ], )?; param.sort_vec = Some(vec![("modified", true)]); test_get_list_sort( runtime, &routes_state, admin_token.as_str(), &mut param, &[ "public", "client_user2", "client_user1", "client_admin2", "client_admin1", ], )?; param.sort_vec = Some(vec![("modified", false)]); test_get_list_sort( runtime, &routes_state, admin_token.as_str(), &mut param, &[ "client_admin1", "client_admin2", "client_user1", "client_user2", "public", ], ) } pub fn get_list_offset_limit(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); for i in 100..302 { add_client_model( runtime, &routes_state, format!("client_{}", i).as_str(), "admin", None, None, )?; } let user_id = "admin"; let admin_token = get_token(runtime, routes_state, user_id)?; let mut param = request::GetClientList { user: Some(user_id.to_string()), ..Default::default() }; test_get_list_offset_limit( runtime, &routes_state, admin_token.as_str(), ¶m, (100..200).collect(), )?; param.limit = Some(0); test_get_list_offset_limit( runtime, &routes_state, admin_token.as_str(), ¶m, (100..302).collect(), )?; param.offset = Some(0); param.limit = Some(5); test_get_list_offset_limit( runtime, &routes_state, admin_token.as_str(), ¶m, (100..105).collect(), )?; param.offset = Some(5); param.limit = Some(0); test_get_list_offset_limit( runtime, &routes_state, admin_token.as_str(), ¶m, (105..302).collect(), )?; param.offset = Some(198); param.limit = Some(50); test_get_list_offset_limit( runtime, &routes_state, admin_token.as_str(), ¶m, (298..302).collect(), )?; param.offset = Some(2); param.limit = Some(105); test_get_list_offset_limit( runtime, &routes_state, admin_token.as_str(), ¶m, (102..207).collect(), )?; param.offset = Some(2); param.limit = None; test_get_list_offset_limit( runtime, &routes_state, admin_token.as_str(), ¶m, (102..202).collect(), ) } pub fn get_list_format_array(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); for i in 100..302 { add_client_model( runtime, &routes_state, format!("client_{}", i).as_str(), "admin", None, None, )?; } let user_id = "admin"; let admin_token = get_token(runtime, routes_state, user_id)?; let mut param = request::GetClientList { user: Some(user_id.to_string()), limit: Some(5), format: Some("array".to_string()), ..Default::default() }; test_get_list_format_array( runtime, &routes_state, admin_token.as_str(), ¶m, (100..105).collect(), )?; param.offset = Some(2); param.limit = Some(105); test_get_list_format_array( runtime, &routes_state, admin_token.as_str(), ¶m, (102..207).collect(), ) } pub fn get_list_invalid_param(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let token = get_token(runtime, routes_state, "admin")?; let uri = "/auth/api/v1/client/list"; let mut query = Map::::new(); query.insert("offset".to_string(), Value::Number((-1).into())); test_get_list_invalid_param(runtime, &routes_state, token.as_str(), uri, &query)?; let mut query = Map::::new(); query.insert("sort".to_string(), Value::String("".to_string())); test_get_list_invalid_param(runtime, &routes_state, token.as_str(), uri, &query)?; let mut query = Map::::new(); query.insert("sort".to_string(), Value::String("created".to_string())); test_get_list_invalid_param(runtime, &routes_state, token.as_str(), uri, &query)?; let mut query = Map::::new(); query.insert( "sort".to_string(), Value::String("created:asc:c".to_string()), ); test_get_list_invalid_param(runtime, &routes_state, token.as_str(), uri, &query)?; let mut query = Map::::new(); query.insert( "sort".to_string(), Value::String("created:asc,name:true".to_string()), ); test_get_list_invalid_param(runtime, &routes_state, token.as_str(), uri, &query)?; let mut query = Map::::new(); query.insert("sort".to_string(), Value::String("a:asc".to_string())); test_get_list_invalid_param(runtime, &routes_state, token.as_str(), uri, &query) } pub fn get_list_invalid_token(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); test_invalid_token( runtime, &routes_state, Method::GET, "/auth/api/v1/client/list", ) } pub fn get_list_invalid_perm(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let method = Method::GET; let uri = "/auth/api/v1/client/list"; let token = get_token(runtime, routes_state, "manager")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "service")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "user")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method, uri) } pub fn get(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); add_client_model(runtime, routes_state, "client_admin", "admin", None, None)?; add_client_model( runtime, routes_state, "client_user", "user", Some("secret".to_string()), Some("image".to_string()), )?; let admin_token = get_token(runtime, routes_state, "admin")?; let public_token = get_token(runtime, routes_state, "public")?; test_get( runtime, &routes_state, admin_token.as_str(), true, "client_admin", )?; test_get( runtime, &routes_state, admin_token.as_str(), true, "client_user", )?; test_get( runtime, &routes_state, public_token.as_str(), false, "public", ) } pub fn get_wrong_id(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let app = Router::new().merge(routes::new_service(routes_state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let admin_token = get_token(runtime, routes_state, "admin")?; let req = server.get("/auth/api/v1/client/id").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", admin_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NOT_FOUND)?; let body: ApiError = resp.json(); if body.code.as_str() != err::E_NOT_FOUND { return Err(format!("unexpected 404 error: {}", body.code.as_str())); } let public_token = get_token(runtime, routes_state, "public")?; let req = server.get("/auth/api/v1/client/client_user1").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", public_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NOT_FOUND)?; let body: ApiError = resp.json(); if body.code.as_str() != err::E_NOT_FOUND { return Err(format!("unexpected 404 error: {}", body.code.as_str())); } Ok(()) } pub fn get_invalid_token(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); test_invalid_token( runtime, &routes_state, Method::GET, "/auth/api/v1/client/id", ) } pub fn get_invalid_perm(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let method = Method::GET; let uri = "/auth/api/v1/client/id"; let token = get_token(runtime, routes_state, "manager")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "service")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "user")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method, uri) } pub fn patch(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let admin_token = get_token(runtime, routes_state, "admin")?; let public_token = get_token(runtime, routes_state, "public")?; test_patch( runtime, &routes_state, admin_token.as_str(), "client_admin", "admin", false, )?; test_patch( runtime, &routes_state, admin_token.as_str(), "client_user", "user", true, )?; test_patch( runtime, &routes_state, public_token.as_str(), "client_public", "public", false, ) } pub fn patch_wrong_id(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); add_client_model(runtime, &routes_state, "client_user", "user", None, None)?; let admin_token = get_token(runtime, routes_state, "admin")?; let public_token = get_token(runtime, routes_state, "public")?; let app = Router::new().merge(routes::new_service(routes_state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let param = request::PatchClient { data: Some(request::PatchClientData { name: Some("name".to_string()), ..Default::default() }), ..Default::default() }; let req = server .patch("/auth/api/v1/client/id") .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", admin_token).as_str()).unwrap(), ) .json(¶m); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NOT_FOUND)?; let body: ApiError = resp.json(); if body.code.as_str() != err::E_NOT_FOUND { return Err(format!("unexpected 404 error: {}", body.code.as_str())); } let req = server .patch("/auth/api/v1/client/client_user") .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", public_token).as_str()).unwrap(), ) .json(¶m); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NOT_FOUND)?; let body: ApiError = resp.json(); if body.code.as_str() != err::E_NOT_FOUND { return Err(format!("unexpected 404 error: {}", body.code.as_str())); } Ok(()) } pub fn patch_invalid_param(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); add_client_model( runtime, &routes_state, "client_public", "public", None, None, )?; let admin_token = get_token(runtime, routes_state, "admin")?; let public_token = get_token(runtime, routes_state, "public")?; test_patch_invalid_param( runtime, &routes_state, admin_token.as_str(), "client_public", None, )?; let param = request::PatchClient { ..Default::default() }; test_patch_invalid_param( runtime, &routes_state, admin_token.as_str(), "client_public", Some(¶m), )?; let param = request::PatchClient { data: Some(request::PatchClientData { redirect_uris: Some(vec!["http://uri1".to_string(), "".to_string()]), ..Default::default() }), ..Default::default() }; test_patch_invalid_param( runtime, &routes_state, admin_token.as_str(), "client_public", Some(¶m), )?; let param = request::PatchClient { data: Some(request::PatchClientData { redirect_uris: Some(vec!["http://uri1".to_string(), ":://".to_string()]), ..Default::default() }), ..Default::default() }; test_patch_invalid_param( runtime, &routes_state, admin_token.as_str(), "client_public", Some(¶m), )?; let param = request::PatchClient { data: Some(request::PatchClientData { scopes: Some(vec!["scope1".to_string(), "".to_string()]), ..Default::default() }), ..Default::default() }; test_patch_invalid_param( runtime, &routes_state, admin_token.as_str(), "client_public", Some(¶m), )?; let param = request::PatchClient { data: Some(request::PatchClientData { scopes: Some(vec!["scope1".to_string(), "scope..abc".to_string()]), ..Default::default() }), ..Default::default() }; test_patch_invalid_param( runtime, &routes_state, admin_token.as_str(), "client_public", Some(¶m), )?; let param = request::PatchClient { regen_secret: Some(true), ..Default::default() }; test_patch_invalid_param( runtime, &routes_state, admin_token.as_str(), "client_public", Some(¶m), )?; let param = request::PatchClient { regen_secret: Some(true), ..Default::default() }; test_patch_invalid_param( runtime, &routes_state, public_token.as_str(), "client_public", Some(¶m), ) } pub fn patch_invalid_token(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); test_invalid_token( runtime, &routes_state, Method::PATCH, "/auth/api/v1/client/id", ) } pub fn patch_invalid_perm(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let method = Method::PATCH; let uri = "/auth/api/v1/client/id"; let token = get_token(runtime, routes_state, "manager")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "service")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "user")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method, uri) } pub fn delete(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); add_client_model( runtime, &routes_state, "client_public", "public", None, None, )?; add_client_model(runtime, &routes_state, "client_user", "user", None, None)?; let admin_token = get_token(runtime, routes_state, "admin")?; let public_token = get_token(runtime, routes_state, "public")?; let app = Router::new().merge(routes::new_service(routes_state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let req = server.delete("/auth/api/v1/client/id").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", admin_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; let _ = get_client_model(runtime, &routes_state, "client_public")?; let req = server .delete("/auth/api/v1/client/client_public") .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", public_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; match get_client_model(runtime, &routes_state, "client_public") { Err(e) => expect(e.contains(NO_ID_ERR_STR)).to_equal(true)?, Ok(_) => return Err("public cannot delete client_public".to_string()), } let req = server.delete("/auth/api/v1/client/client_user").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", public_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; get_client_model(runtime, &routes_state, "client_user")?; let req = server.delete("/auth/api/v1/client/client_user").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", admin_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; match get_client_model(runtime, &routes_state, "client_user") { Err(e) => expect(e.contains(NO_ID_ERR_STR)).to_equal(true)?, Ok(_) => return Err("admin cannot delete client_user".to_string()), } let req = server.delete("/auth/api/v1/client/public").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", public_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::FORBIDDEN)?; let body: ApiError = resp.json(); if body.code.as_str() != err::E_PERM { return Err(format!("unexpected 403 error: {}", body.code.as_str())); } Ok(()) } pub fn delete_invalid_token(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); test_invalid_token( runtime, &routes_state, Method::DELETE, "/auth/api/v1/client/id", ) } pub fn delete_invalid_perm(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let method = Method::DELETE; let uri = "/auth/api/v1/client/id"; let token = get_token(runtime, routes_state, "manager")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "service")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "user")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method, uri) } pub fn delete_user(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); add_client_model( runtime, &routes_state, "client_public", "public", None, None, )?; add_client_model(runtime, &routes_state, "client_user", "user", None, None)?; let token = get_token(runtime, routes_state, "admin")?; let app = Router::new().merge(routes::new_service(routes_state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let req = server.delete("/auth/api/v1/client/user/user").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; match get_client_model(runtime, &routes_state, "client_user") { Err(e) => expect(e.contains(NO_ID_ERR_STR)).to_equal(true)?, Ok(_) => return Err("admin cannot delete user's clients".to_string()), } Ok(()) } pub fn delete_user_invalid_token(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); test_invalid_token( runtime, &routes_state, Method::DELETE, "/auth/api/v1/client/user/id", ) } pub fn delete_user_invalid_perm(context: &mut SpecContext) -> Result<(), String> { let state = context.state.borrow(); let state = state.get(STATE).unwrap(); let runtime = state.runtime.as_ref().unwrap(); let routes_state = state.routes_state.as_ref().unwrap(); let method = Method::DELETE; let uri = "/auth/api/v1/client/user/id"; let token = get_token(runtime, routes_state, "manager")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "public")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "service")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method.clone(), uri)?; let token = get_token(runtime, routes_state, "user")?; test_invalid_perm(runtime, &routes_state, token.as_str(), method, uri) } fn test_post( runtime: &Runtime, state: &routes::State, token: &str, user_id: &str, param: &request::PostClient, expect_code: &str, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let time_before = Utc::now().trunc_subsecs(3); let req = server .post("/auth/api/v1/client") .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ) .json(param); let resp = runtime.block_on(async { req.await }); let time_after = Utc::now().trunc_subsecs(3); let status = resp.status_code(); if status != StatusCode::OK { let body: ApiError = resp.json(); let message = match body.message.as_ref() { None => "", Some(message) => message.as_str(), }; if expect_code == body.code.as_str() { return Ok(()); } return Err(format!( "API not 200, status: {}, code: {}, message: {}", status, body.code.as_str(), message )); } let body: response::PostClient = resp.json(); expect(body.data.client_id.len() > 0).to_equal(true)?; let client_info = match runtime.block_on(async { let cond = QueryCond { client_id: Some(body.data.client_id.as_str()), ..Default::default() }; state.model.client().get(&cond).await }) { Err(e) => return Err(format!("get client model error: {}", e)), Ok(info) => match info { None => return Err("add client then get none client".to_string()), Some(info) => info, }, }; expect(client_info.created_at.ge(&time_before)).to_equal(true)?; expect(client_info.created_at.le(&time_after)).to_equal(true)?; expect(client_info.modified_at.ge(&time_before)).to_equal(true)?; expect(client_info.modified_at.le(&time_after)).to_equal(true)?; match param.credentials { None => expect(client_info.client_secret.is_none()).to_equal(true)?, Some(false) => expect(client_info.client_secret.is_none()).to_equal(true)?, Some(true) => { expect(client_info.client_secret.is_some()).to_equal(true)?; expect(client_info.client_secret.as_ref().unwrap().len() > 0).to_equal(true)?; } } let mut redirect_uris = param.data.redirect_uris.clone(); redirect_uris.sort(); redirect_uris.dedup(); expect(client_info.redirect_uris.clone()).to_equal(redirect_uris)?; let mut scopes = param.data.scopes.clone(); scopes.sort(); scopes.dedup(); expect(client_info.scopes.clone()).to_equal(scopes)?; match param.data.user_id.as_ref() { None => expect(client_info.user_id.as_str()).to_equal(user_id)?, Some(user_id_param) => match user_id { "admin" => expect(client_info.user_id.as_str()).to_equal(user_id_param)?, _ => expect(client_info.user_id.as_str()).to_equal(user_id)?, }, } expect(client_info.name.as_str()).to_equal(param.data.name.as_str())?; match param.data.image.as_ref() { None => expect(client_info.image_url.is_none()).to_equal(true)?, Some(image) => { expect(client_info.image_url.is_some()).to_equal(true)?; expect(client_info.image_url.as_ref().unwrap().as_str()).to_equal(image.as_str())?; } } Ok(()) } fn test_post_invalid_param( runtime: &Runtime, state: &routes::State, token: &str, param: Option<&request::PostClient>, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let mut req = server.post("/auth/api/v1/client").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); if let Some(param) = param { req = req.json(¶m); } let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::BAD_REQUEST)?; let body: ApiError = resp.json(); if body.code.as_str() != err::E_PARAM { return Err(format!("unexpected 400 error: {}", body.code.as_str())); } Ok(()) } fn test_get_count( runtime: &Runtime, state: &routes::State, token: &str, param: Option<&request::GetClientCount>, expect_count: usize, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(&state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let req = server .get("/auth/api/v1/client/count") .add_query_params(¶m) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::OK)?; let body: response::GetClientCount = resp.json(); expect(body.data.count).to_equal(expect_count) } fn test_get_list( runtime: &Runtime, state: &routes::State, token: &str, is_admin: bool, param: Option<&request::GetClientList>, expect_count: usize, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let req = server .get("/auth/api/v1/client/list") .add_query_params(¶m) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::OK)?; let body: response::GetClientList = resp.json(); expect(body.data.len()).to_equal(expect_count)?; let mut name_min = ""; for info in body.data.iter() { if let Err(_) = expect(info.name.as_str().ge(name_min)).to_equal(true) { return Err(format!( "name order error: {} - {}", name_min, info.name.as_str() )); } name_min = info.name.as_str(); expect(info.user_id.is_some()).to_equal(is_admin)?; } Ok(()) } fn test_get_list_sort( runtime: &Runtime, state: &routes::State, token: &str, param: &mut request::GetClientList, expect_ids: &[&str], ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; if let Some(sorts) = param.sort_vec.as_ref() { let sorts: Vec = sorts .iter() .map(|(k, v)| { format!( "{}:{}", k, match v { false => "desc", true => "asc", } ) }) .collect(); if sorts.len() > 0 { param.sort = Some(sorts.join(",")); } } let req = server .get("/auth/api/v1/client/list") .add_query_params(param) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); if let Err(_) = expect(resp.status_code()).to_equal(StatusCode::OK) { let body: ApiError = resp.json(); let message = match body.message.as_ref() { None => "", Some(message) => message.as_str(), }; return Err(format!( "response not 200: /auth/api/v1/client/list, {}, {}", body.code, message )); } let body: response::GetClientList = resp.json(); expect(body.data.len()).to_equal(expect_ids.len())?; let mut index = 0; for data in body.data.iter() { expect(data.client_id.as_str()).to_equal(expect_ids[index])?; index += 1; } Ok(()) } fn test_get_list_offset_limit( runtime: &Runtime, state: &routes::State, token: &str, param: &request::GetClientList, expect_ids: Vec, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(&state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let req = server .get("/auth/api/v1/client/list") .add_query_params(param) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::OK)?; let body: response::GetClientList = resp.json(); expect(body.data.len()).to_equal(expect_ids.len())?; let mut index = 0; for data in body.data.iter() { expect(data.client_id.as_str()) .to_equal(format!("client_{}", expect_ids[index]).as_str())?; index += 1; } Ok(()) } fn test_get_list_format_array( runtime: &Runtime, state: &routes::State, token: &str, param: &request::GetClientList, expect_ids: Vec, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(&state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let req = server .get("/auth/api/v1/client/list") .add_query_params(param) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::OK)?; let body: Vec = resp.json(); expect(body.len()).to_equal(expect_ids.len())?; let mut index = 0; for data in body.iter() { expect(data.client_id.as_str()) .to_equal(format!("client_{}", expect_ids[index]).as_str())?; index += 1; } Ok(()) } fn test_get( runtime: &Runtime, state: &routes::State, token: &str, is_admin: bool, client_id: &str, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(&state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let client_info = get_client_model(runtime, state, client_id)?; let uri = format!("/auth/api/v1/client/{}", client_id); let req = server.get(uri.as_str()).add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::OK)?; let body: response::GetClient = resp.json(); expect(body.data.client_id.as_str()).to_equal(client_info.client_id.as_str())?; expect( DateTime::parse_from_rfc3339(body.data.created_at.as_str()) .unwrap() .timestamp_millis(), ) .to_equal(client_info.created_at.timestamp_millis())?; expect( DateTime::parse_from_rfc3339(body.data.modified_at.as_str()) .unwrap() .timestamp_millis(), ) .to_equal(client_info.modified_at.timestamp_millis())?; match client_info.client_secret.as_ref() { None => expect(body.data.client_secret.is_none()).to_equal(true)?, Some(secret) => { expect(body.data.client_secret.is_some()).to_equal(true)?; expect(body.data.client_secret.as_ref().unwrap().as_str()).to_equal(secret.as_str())?; } } expect(body.data.redirect_uris.clone()).to_equal(client_info.redirect_uris.clone())?; expect(body.data.scopes.clone()).to_equal(client_info.scopes.clone())?; match is_admin { false => expect(body.data.user_id.is_none()).to_equal(true)?, true => { expect(body.data.user_id.is_some()).to_equal(true)?; expect(body.data.user_id.as_ref().unwrap().as_str()) .to_equal(client_info.user_id.as_str())?; } } expect(body.data.name.as_str()).to_equal(client_info.name.as_str())?; match client_info.image_url.as_ref() { None => expect(body.data.image.is_none()).to_equal(true)?, Some(image) => { expect(body.data.image.is_some()).to_equal(true)?; expect(body.data.image.as_ref().unwrap().as_str()).to_equal(image.as_str())?; } } Ok(()) } fn test_patch( runtime: &Runtime, state: &routes::State, token: &str, client_id: &str, user_id: &str, use_secret: bool, ) -> Result<(), String> { let secret = match use_secret { false => None, true => Some("secret".to_string()), }; add_client_model(runtime, state, client_id, user_id, secret, None)?; let user_token = get_token_client_id( runtime, state, user_id, client_id, match use_secret { false => None, true => Some("secret"), }, Some(client_id), )?; let app = Router::new().merge(routes::new_service(state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let time_before = Utc::now().trunc_subsecs(3); let mut redirect_uris = vec![ "http://uri2".to_string(), "http://uri1".to_string(), "http://uri2".to_string(), ]; let mut scopes = vec![ "scope2".to_string(), "scope1".to_string(), "scope2".to_string(), ]; let body = request::PatchClient { data: Some(request::PatchClientData { redirect_uris: Some(redirect_uris.clone()), scopes: Some(scopes.clone()), name: Some("name changes".to_string()), image: Some(Some("image url".to_string())), }), regen_secret: match use_secret { false => None, true => Some(true), }, }; let req = server .patch(format!("/auth/api/v1/client/{}", client_id).as_str()) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ) .json(&body); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; let time_after = Utc::now().trunc_subsecs(3); let client_info = get_client_model(runtime, state, client_id)?; expect(client_info.modified_at.ge(&time_before)).to_equal(true)?; expect(client_info.modified_at.le(&time_after)).to_equal(true)?; redirect_uris.sort(); redirect_uris.dedup(); expect(client_info.redirect_uris.clone()).to_equal(redirect_uris)?; scopes.sort(); scopes.dedup(); expect(client_info.scopes.clone()).to_equal(scopes)?; expect(client_info.name.as_str()).to_equal("name changes")?; expect(client_info.image_url.is_some()).to_equal(true)?; expect(client_info.image_url.as_ref().unwrap().as_str()).to_equal("image url")?; if use_secret { expect(client_info.client_secret.is_some()).to_equal(true)?; expect(client_info.client_secret.as_ref().unwrap().as_str()).to_not_equal("secret")?; } let req = server.get("/auth/api/v1/user").add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", user_token).as_str()).unwrap(), ); let resp = runtime.block_on(async { req.await }); match use_secret { false => expect(resp.status_code()).to_equal(StatusCode::OK)?, true => expect(resp.status_code()).to_equal(StatusCode::UNAUTHORIZED)?, } let body = request::PatchClient { data: Some(request::PatchClientData { redirect_uris: Some(vec![]), scopes: Some(vec![]), ..Default::default() }), ..Default::default() }; let req = server .patch(format!("/auth/api/v1/client/{}", client_id).as_str()) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ) .json(&body); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; let client_info = get_client_model(runtime, state, client_id)?; expect(client_info.redirect_uris.len()).to_equal(0)?; expect(client_info.scopes.len()).to_equal(0)?; expect(client_info.image_url.is_some()).to_equal(true)?; expect(client_info.image_url.as_ref().unwrap().as_str()).to_equal("image url")?; let body = request::PatchClient { data: Some(request::PatchClientData { image: Some(None), ..Default::default() }), ..Default::default() }; let req = server .patch(format!("/auth/api/v1/client/{}", client_id).as_str()) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ) .json(&body); let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::NO_CONTENT)?; let client_info = get_client_model(runtime, state, client_id)?; if use_secret { expect(client_info.client_secret.is_some()).to_equal(true)?; } expect(client_info.image_url.is_none()).to_equal(true) } fn test_patch_invalid_param( runtime: &Runtime, state: &routes::State, token: &str, client_id: &str, param: Option<&request::PatchClient>, ) -> Result<(), String> { let app = Router::new().merge(routes::new_service(state)); let server = match TestServer::new(app) { Err(e) => return Err(format!("new server error: {}", e)), Ok(server) => server, }; let mut req = server .patch(format!("/auth/api/v1/client/{}", client_id).as_str()) .add_header( header::AUTHORIZATION, HeaderValue::from_str(format!("Bearer {}", token).as_str()).unwrap(), ); if let Some(param) = param { req = req.json(¶m); } let resp = runtime.block_on(async { req.await }); expect(resp.status_code()).to_equal(StatusCode::BAD_REQUEST)?; let body: ApiError = resp.json(); if body.code.as_str() != err::E_PARAM { return Err(format!("unexpected 400 error: {}", body.code.as_str())); } Ok(()) } fn get_client_model( runtime: &Runtime, state: &routes::State, client_id: &str, ) -> Result { match runtime.block_on(async { let cond = QueryCond { client_id: Some(client_id), ..Default::default() }; state.model.client().get(&cond).await }) { Err(e) => return Err(format!("get client model info error: {}", e)), Ok(client) => match client { None => return Err(format!("{} {}", NO_ID_ERR_STR, client_id)), Some(client) => return Ok(client), }, } } fn add_client_model( runtime: &Runtime, state: &routes::State, client_id: &str, user_id: &str, secret: Option, image: Option, ) -> Result<(), String> { match runtime.block_on(async { let mut client = create_client(client_id, user_id, secret.clone()); if secret.is_some() { client.scopes = vec![client_id.to_string()]; } if let Some(image) = image.as_ref() { client.image_url = Some(image.to_string()); } state.model.client().add(&client).await }) { Err(e) => Err(format!("add client model info error: {}", e)), Ok(_) => Ok(()), } } /// Returns (total_clients, user_client) tuple. fn count_list_dataset(runtime: &Runtime, state: &routes::State) -> Result<(usize, usize), String> { let now = Utc::now(); let mut client = create_client("client_admin1", "admin", None); client.created_at = now; client.modified_at = now + TimeDelta::try_milliseconds(3).unwrap(); runtime.block_on(async { if let Err(e) = state.model.client().add(&client).await { return Err(format!("add client {} error: {}", client.client_id, e)); } Ok(()) })?; client.client_id = "client_admin2".to_string(); client.created_at = now + TimeDelta::try_milliseconds(1).unwrap(); client.modified_at = now + TimeDelta::try_milliseconds(2).unwrap(); client.name = "client_admin2".to_string(); runtime.block_on(async { if let Err(e) = state.model.client().add(&client).await { return Err(format!("add client {} error: {}", client.client_id, e)); } Ok(()) })?; client.client_id = "client_user1".to_string(); client.created_at = now + TimeDelta::try_milliseconds(2).unwrap(); client.modified_at = now + TimeDelta::try_milliseconds(1).unwrap(); client.user_id = "user".to_string(); client.name = "client_user2".to_string(); runtime.block_on(async { if let Err(e) = state.model.client().add(&client).await { return Err(format!("add client {} error: {}", client.client_id, e)); } Ok(()) })?; client.client_id = "client_user2".to_string(); client.created_at = now + TimeDelta::try_milliseconds(3).unwrap(); client.modified_at = now; client.client_secret = Some("secret".to_string()); client.name = "client_user1".to_string(); runtime.block_on(async { if let Err(e) = state.model.client().add(&client).await { return Err(format!("add client {} error: {}", client.client_id, e)); } Ok(()) })?; Ok((5, 2)) }