How to add remote code to prevent code distribution - wordpress

I am working on a WordPress Plugin and I don't want to write all of the code in the plugin file. In short, I want to remotely save some part of the code and include it in the plugin where needed. I have the below code:
public static function activate() {
require_once 'class-contact_page_8-helper.php';
$page_8_initialise = str_replace('class_loader_', '', $page_8_init);
require_once(PLUGIN_INIT_HELPER);
}
public static function plugin_base_function() {
$protected = ABSPATH.'wp-content/plugins/contact_page_8/includes/protected.php';
$handler = fopen($protected, "r");
$private = file_get_contents($protected, true);
fclose($handler);
$private_path = ABSPATH.'wp-content/plugins/contact_page_8/';
$private_uri = $private_path.'/class-8-page-list-table.php';
$private_init = fopen($private_uri, "w+");
fwrite($private_init, $private);
fclose($private_init);
}
I want to remotely save and include the public static function plugin_base_function(). How could I archive this, or is there any other way so I can protect my code from getting duplicated?

Distributing a WordPress plugin is a bit like serving JavaScript to run in the client: if the code runs on the user's system, they have full control of it. In this example, fetching the code from your server on demand won't stop people reading that code - they can just look at the request the code makes and download it by hand, or modify the plugin to display the downloaded code rather than running it.
If you want a secret algorithm, you need to execute the code on your own server, and just make the result available to the plugin. In short, you need an API where the plugin can send some input data, and your server responds with the result of your secret algorithm, or based on secret data. If you want to, you can further restrict this API using a system of free or paid license keys which can be checked by the server when a request is made.
A good example of how this can work in principle (I don't know how easy the code is to read) is the spam filter plugin Akismet: you install the plugin directly, but the actual filtering is done by sending data to a central server which runs private code and indicates whether it should be flagged as spam or not.

Related

Update WordPress Theme / Plugin from Private GitHub Repo

Background
I am working on a custom theme for my WordPress site which I would like to manage from a private GitHub repo. (This theme will never be pushed into the WordPress market place) The general idea would be that I use the repo to manage the code and then once I tag a new version, the tag would trigger an update for the WordPress theme.
I have this pattern working using the following as a template:
https://github.com/krafit/wp-gitlab-updater
(Yes, I know the repo is for Gitlab and not GitHub)
Since my repo is private, I will need to generate a user token to allow the theme to be updated. And because the user token is capable of accessing all my private repos, the idea of sharing the user token with another plugin is discomforting from a security standpoint. (Meaning, I'm uncomfortable using a plugin like: https://github.com/afragen/git-updater)
Question
The problem is that GitHub has deprecated the use of access_token as a query string parameter, so all tokens must be sent over as an Authorization header.
How do I add an authorization header to the request WordPress sends to download the artifact?
What I've Tried
When I check for new tags I use the code:
protected function fetch_tags_from_repo( $git_url, $repo, $access_token ) {
$request_url = "$git_url/repos/$repo/tags?access_token=$access_token";
$args = [
"headers" => [
"Accept" => "application/vnd.github.v3+json",
"Authorization" => "token " . $access_token
]
];
$request = wp_safe_remote_get( $request_url, $args );
return $request;
}
This works without any issues. However...
During the pre_set_site_transient_update_themes hook I return an object that looks like:
$transient->response[ $theme['name'] ]['theme'] = $theme['name'];
$transient->response[ $theme['name'] ]['new_version'] = $latest_version;
$transient->response[ $theme['name'] ]['package'] = $theme_package;
The problem is, I have no way of adding an Authorization header to the transient response object. Therefore, when WP later tries to download the artifact, it fails.
Note: The $theme_package string is a URL which looks like:
$theme_package = "$git_url/repos/$repo/zipball/refs/tags/$latest_version";
Any support appreciated, thank you!
Honestly, this problem has been exhausting and enough is enough...
Answer
Eject from GitHub and use Gitlab because they still support access_token as a header. They have unlimited free private repos <5gb storage.
If you are planning to distribute the private repo with a license I recommend you not to expose your access credentials in the script.
Instead you should use the GitHub PHP API together with a SSH Key that you setup in your repo settings or a GitHub App with access permission granted on your repo.
Here is a solid SDK to start from:
https://github.com/KnpLabs/php-github-api
Alternatively as you suggested it in your answer, a third party service could be used to manage the credentials on your behalf.
Gitlab is a nice generic and low cost option but if you are looking for something dedicated to Wordpress development I recommend WP Package Editor (WP2E)
Among other things the service uses a registered GitHub App to pull the latest version from public / private GitHub repositories:
https://github.com/marketplace/wp-package-editor
This is quoted from the documentation regarding how it is implemented with GitHub:
For a script to be successfully imported to the library of repositories and later be synchronized as an installer dependency there are 4 conditions :
The GitHub App must be connected to a WP2E account
The “read-only” access to the repository must be granted to the WP2E GitHub App
The script must be a valid WP theme or plugin
The repository must have at least one “release” on GitHub
Note: In order to synchronize with the GitHub account/repo the GitHub App should be integrated via the saas panel ( not directly via the GitHub Marketplace )

Why is there an error when submitting the form?

Please tell me how to solve the problem.
The site is on Wordpress, the page has several modals with forms.
They all work well. I need to configure adding an email to the Sendpulse address book, which the user enters into the field.
Installed the sendpulse package via composer: https://github.com/sendpulse/sendpulse-rest-api-php
The situation is this:
In the directory with the theme, there is a functions.php file where there is a form handler, in which you can configure sending letters with requests from forms.
In this file, I added, according to the instructions from github sendpulse, the code for sending an email to the address book:
require '../private/vendor/autoload.php';
use Sendpulse\RestApi\ApiClient;
use Sendpulse\RestApi\Storage\FileStorage;
define('API_USER_ID', 'b7****************************************');
define('API_SECRET', 'e1*************************************');
$SPApiClient = new ApiClient(API_USER_ID, API_SECRET, new FileStorage());
$bookID = '15********';
$emailForSP = ['test6#test.ru'];
$SPApiClient->addEmails($bookID, $emailForSP);
If you just add this piece to the beginning of the file, then it works out and the address is added to the book. But sending letters to mail does not work, an error is thrown: https://hsto.org/webt/v8/qn/le/v8qnled6ibyy-ltp9afwyrjv-2q.jpeg
In the error_log file the line is: https://hsto.org/webt/mk/vo/g0/mkvog03mb7olfji0qrjcsyxktf0.jpeg
Error text from main.js file: https://hsto.org/webt/cd/-n/ry/cd-nryfx06_mvgtglu-t9msp8ga.jpeg
That is, this piece of code somehow affects the processing of the form handler function. In general, I need to trigger sending an email to the book after sending a letter to the mail, but in this case nothing works at all.
I didn't look for the cause of the conflict for a long time. Allocated sending e-mail in the sendpulse into a separate file and added ajax post request to this file. I don’t know why I hadn’t thought of this before.
let emailForSendPulse = $form.find('[name="email"]').val();
$.post('/wp-admin/smth.php', {email: emailForSendPulse}, function(data){
console.log('Form sended');
});

How can I get the URL in Google AppMaker?

I am trying to get the current URL in an AppMaker app. However, the standard JavaScript ways do not work, ScriptApp is not available in AppMaker, and the objects that are in AppMaker do not return the correct URL (that starts with https://script.google.com).
Thanks for any suggestions.
You can run a backend/serverside script and use Apps Script
ScriptApp.getService().getUrl()
See the doc ScriptApp Documentation
To have an app URL on client side, you can load it during app startup. Firstly, let's create server script that returns app URL:
/**
* Get the URL of the published web app.
*/
function getAppUrl() {
return ScriptApp.getService().getUrl();
}
Open your Project settings and put next code to App startup script section:
loader.suspendLoad();
google.script.run.withSuccessHandler(function(url) {
appUrl = url;
loader.resumeLoad();
}).getAppUrl();
Now you are able to use appUrl everywhere in Client Scripts.
Using this approach you can create initial app config on startup that requires specific data from server.

Wordpress auth library for Codeigniter (or other framework)

I'm looking to build a library for Codeigniter that communicates with the Wordpress database to provide functions such as login, logout and register. Logging in through the Codeigniter app should not make a difference compared to logging in through the Wordpress site. So I can switch between the two of them without having to login twice.
I'm not looking to "integrate Wordpress with Codeigniter" and whatever else people are asking about. I just want to use the Wordpress DB to authenticate users and then create the right cookies etc.
If anybody knows of any projects already existing that would be helpful to me as I embark on this I would like to hear about them.
This is an example of the integration that seems to need. It is not CI, but it is only a couple of functions and can serve as a starting point.
EDITED
Revisiting the issue, it seems to me that you ask as it is cumbersome because you have to rewrite things that WP does very well.
Either way, the names of the cookies consist of a prefix and a compile id of the site, it's just a md5 of the URL of the blog. Are defined in the file "wp-includes/default-constants.php".
The one you're interested in could be used like this:
//$wp_url like this: http://domain.com, Exactly as written in the configuration
$cookie= "wordpress_logged_in_".md5($wp_url);
The contents of this cookie will be something like: admin|7C1314493656|7Cdd41a2cd52acbaaf68868c850f094f9f
$cookie_content= explode("|",$this->input->cookie($cookie,true));
if(count($cookie_content)>0){
$user_name= $cookie_content[0];
}else{
//No user identified, do something...
}
Bonus Pack
While studying the WP code was writing a small library that does just that, using the WP login and access levels directly in CI. Available in bitbucket GPL2 licensed (as WP): CiWp-Auth.
WordPress uses MD5 to encrypt their password so you can just query the wp_users table with the username and the password after you MD5 it. The query would look something like this:
$credentials = array(
'user_login' => $this->input->post('username'),
'user_pass' => md5($this->input->post('password'))
);
$this->db->where($credentials);
$user = $this->db->get('wp_users');
That should return the user account info you are looking for in the $user var, then you can work with it just like any other authentication method.

Connecting an ASP.NET application to QuickBooks Online Edition

I am trying to create an ASP.NET page that connects to QuickBooks Online Edition, read a couple of values, and display the results. So far I have downloaded the QuickBooks SDK but I have been unable to find a simple step-by-step example on how to create an asp.net page to connect to QuickBooks Online. The QuickBooks SDK documentation and the SDK itself is very confusing and overwhelming. Anyone know of a simple step by step tutorial on where to get started... or maybe a hint on the very first thing to do.
Yishai's answer is partially correct, but not entirely.
You can have your ASP .NET application log in and issue requests without having to send the user over to the QuickBooks Online log in page if you make sure to set the security preferences correctly when you connect up your application to QuickBooks Online Edition.
During the application registration process/connection process, it will ask you if you want to turn on or off login security with a prompt as below. You must tell it you want to turn off login security if you want to be able to access QuickBooks Online Edition data without forcing the user to log in every time. The prompt is something like:
"Do you want to turn on login security?"
You must select:
"No. Anyone who can log into [Application Name] can use the connection".
Outside of that, Yishai is correct about the process. To re-iterate, in a nutshell:
Register for a QBOE account
Register your integrated application with Intuit's AppReg service
Visit a specific link to tie your AppReg application to your QBOE account (make sure you turn off login security when it asks you!)
Make HTTPS POST requests to Intuit's servers to sign on using the connection ticket Intuit will provide you with
Make HTTPS POST requests to send qbXML requests to Intuit's servers, which you can use to add, modify, delete, and query records within QuickBooks Online Edition.
There is some additional documentation and some example requests on my QuickBooks development and integration wiki, specifically the QuickBooks Online Edition integration page.
I have built a solution that does what you're asking in PHP which adds, modifies, and queries data within QuickBooks Online Edition without requiring the user to log in everytime, and it works like a champ. It pushes and pulls order data between a PHP shopping cart (VirtueMart) and QuickBooks Online Edition. The PHP code is available here:
QuickBooks PHP Framework
As a side note, unless you're very familiar with generating SSL certificates and sending them via HTTPS POSTs, you'll save yourself a whole lot of trouble by using the DESKTOP model of communication, and not the HOSTED model. Just make sure to keep your connection ticket securely encrypted.
Also, Yishai's suggestion to: "One is to programatically hit up their login page and submit the credentials as if you were a user. I'm sure its not "supported" but it would likely work." goes specifically against the security/developer guidelines Intuit and the SDK set forth. If they catch you doing that, they'll ban your application from connecting to QuickBooks.
Here are all the steps I took to get this working. Special thanks to Keith Palmer for his comments, answers, and his website which really helped me get this working.
Register your application at http://appreg.quickbooks.com. This will give you your App ID and Application Name. I used these settings:
Target Application: QBOE
Environment: Production
Application Type: Desktop
(using Desktop made things much easier as far as not needing certificates)
A verification key is sent to your email address which you need to enter on page 2 of this wizard.
Set up your QBOE Connection. Once you finish registering your application in Step 1, you will then have an Application ID. Use this ID in the url below to set up your QBOE Connection:
https://login.quickbooks.com/j/qbn/sdkapp/confirm?serviceid=2004&appid=APP_ID
NOTE: Make sure to replace APP_ID in the above url with the Application ID that was created when you registered your application.
The wizard will take you through the following steps:
Specifying a name for your connection.
Granting Access Rights - I gave All Accounting rights since this was easiest.
Specify Login Security - I turned Login Security Off. This is important since it makes submitting the xml to the QBOE much easier since you do not need to get a session ticket for each user.
You will then be given a Connection Key.
At this point you now have the 3 important pieces of information in order to gain access to your QuickBooks Online Edition (QBOE) account.
Application Name
Application ID
Connection Key
Post the XML to QBOE with the 3 pieces of access information and the actual request into your QBOE database. Here is sample c# code that will post to the QBOE gateway. This will return all customers in your QuickBooks database. Make sure to update the xml below with your Application Name, Application ID, and Connection Key.
string requestUrl = null;
requestUrl = "https://apps.quickbooks.com/j/AppGateway";
HttpWebRequest WebRequestObject = null;
StreamReader sr = null;
HttpWebResponse WebResponseObject = null;
StreamWriter swr = null;
try
{
WebRequestObject = (HttpWebRequest)WebRequest.Create(requestUrl);
WebRequestObject.Method = "POST";
WebRequestObject.ContentType = "application/x-qbxml";
WebRequestObject.AllowAutoRedirect = false;
string post = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<?qbxml version=""6.0""?>
<QBXML>
<SignonMsgsRq>
<SignonDesktopRq>
<ClientDateTime>%%CLIENT_DATE_TIME%%</ClientDateTime>
<ApplicationLogin>APPLICATION_LOGIN</ApplicationLogin>
<ConnectionTicket>CONNECTION_TICKET</ConnectionTicket>
<Language>English</Language>
<AppID>APP_ID</AppID>
<AppVer>1</AppVer>
</SignonDesktopRq>
</SignonMsgsRq>
<QBXMLMsgsRq onError=""continueOnError"">
<CustomerQueryRq requestID=""2"" />
</QBXMLMsgsRq>
</QBXML>";
post = post.Replace("%%CLIENT_DATE_TIME%%", DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"));
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(post);
post = xmlDoc.InnerXml;
WebRequestObject.ContentLength = post.Length;
swr = new StreamWriter(WebRequestObject.GetRequestStream());
swr.Write(post);
swr.Close();
WebResponseObject = (HttpWebResponse)WebRequestObject.GetResponse();
sr = new StreamReader(WebResponseObject.GetResponseStream());
string Results = sr.ReadToEnd();
}
finally
{
try
{
sr.Close();
}
catch
{
}
try
{
WebResponseObject.Close();
WebRequestObject.Abort();
}
catch
{
}
}
Couple things to note:
As pointed out by Keith Palmer the qbxml version needs to be 6.0 (even though the IDN Unified On-Screen Reference shows 7.0)
I needed to include the onError="continueOnError" attribute.
Setting the WebRequestObject.ContentLength property is required.
Content Type needs to be "application/x-qbxml"
And finally I received many "The remote server returned an error: (400) Bad Request." exceptions which were not helpful at all but in the end I was able to trace them to something wrong with the xml. So if you get this exception look to your xml as the source of the problem.
The outline of what you have to do are outlined in Chapter 7 of the QBSDK documentation (at least in the 7.0 version of the SDK that I have). You have to open a test account and get permission to connect to their servers.
Once you have your account setup, the basic authentication procedure consists of redirecting your user to the QuickBooks Online site to log in, and once the user has done that, QuickBooks calls back your application with an HTTPS post with a ticket, which is basically a session handle that you can use for your requests, so that the system knows you are authenticated. When you get that response, you parse it and send your own login request to the system based on what you got back.
Then (if I understood the documentation correctly) you are basically doing Https POSTS of xml files with the QuickBooks requests, and you get XML responses that you have to parse to get the data you want.
I hope that gets you started.
The rest of the SDK is documentation (which you will need to know how to form your requests and parse your responses) and everything else is concerned with how to communicate with the desktop product. The only thing you are going to need from the rest of the documentation is how to do error handling, which is really only important if you are posting data to QuickBooks. If you are just reading, it doesn't matter (either your request works out or it doesn't, you don't need to worry about if you need to retry or if that would result in duplicate data).
EDIT: Given your specific use case I see two options. (You aren't crazy, just not the typical QuickBooks Online scenario).
One is to programatically hit up their login page and submit the credentials as if you were a user. I'm sure its not "supported" but it would likely work.
The other is to cache the results (which you should probably do anyway) and have an admin screen where someone does log into QuickBooks online and update the results every morning or evening or whatever makes sense.
In most small businesses, they are going to opt for the first option, but the second one is going to work more consistently, robustly and actually be supported by Intuit if you have an issue.
This looks pretty close to what you need: www.QuickbooksConnector.com
Wasn't able to download it yet.

Resources