Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement wp-login.php based cookie authentication #327

Open
wants to merge 28 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
de6b9a0
Implement wp-login.php based cookie authentication
crazytonyli Sep 27, 2024
97c9bbf
Add a few integration tests for cookie authentication
crazytonyli Sep 30, 2024
b3e0d05
Implement authentication methods in dedicated types
crazytonyli Oct 6, 2024
5d977f2
Save fetched nonce
crazytonyli Oct 14, 2024
1366c95
Merge branch 'trunk' into wp-login-cookie-authentication
crazytonyli Oct 15, 2024
f8e73e6
Add TestCredentials.admin_account_password
crazytonyli Oct 15, 2024
2721dfd
Do not mutate request in Authenticator
crazytonyli Oct 15, 2024
aeb2ae5
Add unit tests for derived url functions
crazytonyli Oct 15, 2024
a0613fd
Format code
crazytonyli Oct 15, 2024
083a7d3
Add a constrant for "application/x-www-form-urlencoded"
crazytonyli Oct 15, 2024
bf61854
Derive Default on InnerRequestBuilder
crazytonyli Oct 15, 2024
2cba32e
Refactor authenticator to extra authentication logic
crazytonyli Oct 15, 2024
52c8a5c
Remove AuthenticationError
crazytonyli Oct 15, 2024
06ef60d
Add an unit test to verify nonce is reused across multiple requests
crazytonyli Oct 22, 2024
527c176
Prevent repeatedly fetching nonce when sending concurrent requests
crazytonyli Oct 22, 2024
53f6f66
Replace empty default value with assertions
crazytonyli Oct 22, 2024
1d0c91c
Clone instead of re-creating ApiBaseUrl
crazytonyli Oct 22, 2024
9ca673b
Run cookie authentication tests in parallel
crazytonyli Oct 22, 2024
776ba65
Add an API to document RequestExecutor must support cookie-jar
crazytonyli Oct 22, 2024
3112f32
Update function parameters
crazytonyli Oct 22, 2024
3fcdda3
Rename get_reset_nonce to fetch_rest_nonce
crazytonyli Oct 22, 2024
0afe44d
Merge branch 'trunk' into wp-login-cookie-authentication
crazytonyli Oct 23, 2024
c30f3fb
Add more test cases to wp-login and rest-nonce unit tests
crazytonyli Oct 23, 2024
a5884e7
Simplify deriving wp-login url implementation
crazytonyli Oct 23, 2024
86caf42
Simplify Authenticator trait design
crazytonyli Oct 24, 2024
2bf1ec5
Fix a compiling issue
crazytonyli Oct 24, 2024
40404ff
Pass previous authentication header instead of the request object
crazytonyli Oct 24, 2024
012a36a
Add unit tests for CookieAuthenticator
crazytonyli Oct 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ rstest = "0.21"
rstest_reuse = "0.7.0"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7"
serial_test = "3.1"
strum = "0.26"
strum_macros = "0.26"
Expand Down
19 changes: 10 additions & 9 deletions scripts/setup-test-site.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set -e

# This script sets up a WordPress test site on the `wordpress` docker image.
# This script sets up a WordPress test site on the `wordpress` docker image.
# You might wonder "why not do this work once, then just import the database for each run?"
# We do each step each time for each build because we're trying to get a "mint" condition site
# for each WordPress version – if there are issues with DB migrations, different default themes
Expand Down Expand Up @@ -41,13 +41,16 @@ echo "--- :wordpress: Setting up WordPress"
wp core version --extra
wp --info

ADMIN_USERNAME="[email protected]"
ADMIN_ACCOUNT_PASSWORD="strongpassword"

## Install WordPress
wp core install \
--url=localhost \
--title=my-test-site \
--admin_user=[email protected] \
--admin_email=[email protected] \
--admin_password=strongpassword \
--admin_user=$ADMIN_USERNAME \
--admin_email=$ADMIN_USERNAME \
--admin_password=$ADMIN_ACCOUNT_PASSWORD \
--skip-email

## Ensure URLs work as expected
Expand Down Expand Up @@ -76,7 +79,6 @@ wp user create test_author [email protected] --role=author

create_test_credentials () {
local SITE_URL
local ADMIN_USERNAME
local ADMIN_PASSWORD_UUID
local ADMIN_PASSWORD
local SUBSCRIBER_USERNAME
Expand All @@ -85,9 +87,8 @@ create_test_credentials () {
local TRASHED_POST_ID
local PASSWORD_PROTECTED_POST_ID
SITE_URL="http://localhost"
ADMIN_USERNAME="[email protected]"
ADMIN_PASSWORD="$(wp user application-password create [email protected] test --porcelain)"
ADMIN_PASSWORD_UUID="$(wp user application-password list [email protected] --fields=uuid --format=csv | sed -n '2 p')"
ADMIN_PASSWORD="$(wp user application-password create $ADMIN_USERNAME test --porcelain)"
ADMIN_PASSWORD_UUID="$(wp user application-password list $ADMIN_USERNAME --fields=uuid --format=csv | sed -n '2 p')"
SUBSCRIBER_USERNAME="themedemos"
SUBSCRIBER_PASSWORD="$(wp user application-password create themedemos test --porcelain)"
SUBSCRIBER_PASSWORD_UUID="$(wp user application-password list themedemos --fields=uuid --format=csv | sed -n '2 p')"
Expand All @@ -104,6 +105,7 @@ create_test_credentials () {
jo -p \
site_url="$SITE_URL" \
admin_username="$ADMIN_USERNAME" \
admin_account_password="$ADMIN_ACCOUNT_PASSWORD" \
admin_password="$ADMIN_PASSWORD" \
admin_password_uuid="$ADMIN_PASSWORD_UUID" \
subscriber_username="$SUBSCRIBER_USERNAME" \
Expand Down Expand Up @@ -131,4 +133,3 @@ wp option update timezone_string "America/New_York"
cp -rp wp-content/plugins wp-content/plugins-backup

wp db export --add-drop-table wp-content/dump.sql

1 change: 1 addition & 0 deletions wp_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ paste = { workspace = true }
regex = { workspace = true }
serde = { workspace = true, features = [ "derive" ] }
serde_json = { workspace = true }
serde_urlencoded = { workspace = true }
thiserror = { workspace = true }
uniffi = { workspace = true }
uuid = { workspace = true, features = [ "v4" ] }
Expand Down
73 changes: 46 additions & 27 deletions wp_api/src/api_client.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
use crate::request::{
endpoint::{
application_passwords_endpoint::{
ApplicationPasswordsRequestBuilder, ApplicationPasswordsRequestExecutor,
},
plugins_endpoint::{PluginsRequestBuilder, PluginsRequestExecutor},
post_types_endpoint::{PostTypesRequestBuilder, PostTypesRequestExecutor},
posts_endpoint::{PostsRequestBuilder, PostsRequestExecutor},
site_settings_endpoint::{SiteSettingsRequestBuilder, SiteSettingsRequestExecutor},
users_endpoint::{UsersRequestBuilder, UsersRequestExecutor},
wp_site_health_tests_endpoint::{
WpSiteHealthTestsRequestBuilder, WpSiteHealthTestsRequestExecutor,
},
ApiBaseUrl,
},
RequestExecutor,
};
use crate::{
api_client_generate_api_client, api_client_generate_endpoint_impl,
api_client_generate_request_builder, ParsedUrl, WpAuthentication,
};
use crate::{
authenticator::{
ApplicationPasswordAuthenticator, AuthenticatedRequestExecutor, Authenticator,
CookieAuthenticator, NilAuthenticator,
},
request::{
endpoint::{
application_passwords_endpoint::{
ApplicationPasswordsRequestBuilder, ApplicationPasswordsRequestExecutor,
},
plugins_endpoint::{PluginsRequestBuilder, PluginsRequestExecutor},
post_types_endpoint::{PostTypesRequestBuilder, PostTypesRequestExecutor},
posts_endpoint::{PostsRequestBuilder, PostsRequestExecutor},
site_settings_endpoint::{SiteSettingsRequestBuilder, SiteSettingsRequestExecutor},
users_endpoint::{UsersRequestBuilder, UsersRequestExecutor},
wp_site_health_tests_endpoint::{
WpSiteHealthTestsRequestBuilder, WpSiteHealthTestsRequestExecutor,
},
ApiBaseUrl,
},
RequestExecutor,
},
};
use std::sync::Arc;

#[derive(Debug, uniffi::Object)]
Expand All @@ -29,9 +35,9 @@ struct UniffiWpApiRequestBuilder {
#[uniffi::export]
impl UniffiWpApiRequestBuilder {
#[uniffi::constructor]
pub fn new(site_url: Arc<ParsedUrl>, authentication: WpAuthentication) -> Self {
pub fn new(site_url: Arc<ParsedUrl>) -> Self {
Self {
inner: WpApiRequestBuilder::new(site_url, authentication),
inner: WpApiRequestBuilder::new(site_url),
}
}
}
Expand All @@ -48,11 +54,10 @@ pub struct WpApiRequestBuilder {
}

impl WpApiRequestBuilder {
pub fn new(site_url: Arc<ParsedUrl>, authentication: WpAuthentication) -> Self {
pub fn new(site_url: Arc<ParsedUrl>) -> Self {
let api_base_url: Arc<ApiBaseUrl> = Arc::new(site_url.inner.clone().into());
api_client_generate_request_builder!(
api_base_url,
authentication;
api_base_url;
application_passwords,
plugins,
post_types,
Expand Down Expand Up @@ -102,9 +107,25 @@ impl WpApiClient {
) -> Self {
let api_base_url: Arc<ApiBaseUrl> = Arc::new(site_url.inner.clone().into());

let authenticator: Arc<dyn Authenticator> = match &authentication {
WpAuthentication::AuthorizationHeader { token } => {
Arc::new(ApplicationPasswordAuthenticator::new(token.clone()))
}
WpAuthentication::UserAccount { login } => Arc::new(CookieAuthenticator::new(
site_url.inner.clone().into(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we clone the Arc<ParsedUrl> instead of inner?

login.clone(),
request_executor.clone(),
)),
WpAuthentication::None => Arc::new(NilAuthenticator {}),
};

let request_executor = Arc::new(AuthenticatedRequestExecutor::new(
authenticator,
request_executor,
));

api_client_generate_api_client!(
api_base_url,
authentication,
request_executor;
application_passwords,
plugins,
Expand Down Expand Up @@ -161,12 +182,11 @@ macro_rules! api_client_generate_endpoint_impl {

#[macro_export]
macro_rules! api_client_generate_request_builder {
($api_base_url:ident, $authentication:ident; $($element:expr),*) => {
($api_base_url:ident; $($element:expr),*) => {
paste::paste! {
Self {
$($element: [<$element:camel RequestBuilder>]::new(
$api_base_url.clone(),
$authentication.clone(),
)
.into(),)*
}
Expand All @@ -176,12 +196,11 @@ macro_rules! api_client_generate_request_builder {

#[macro_export]
macro_rules! api_client_generate_api_client {
($api_base_url:ident, $authentication:ident, $request_executor:ident; $($element:expr),*) => {
($api_base_url:ident, $request_executor:ident; $($element:expr),*) => {
paste::paste! {
Self {
$($element: [<$element:camel RequestExecutor>]::new(
$api_base_url.clone(),
$authentication.clone(),
$request_executor.clone(),
)
.into(),)*
Expand Down
Loading