Age | Commit message (Collapse) | Author |
|
|
|
|
|
Add a script to install dependencies and perform initial application
setup.
Another script sources the environment file.
|
|
Running
$ mysql -u user dome_key < 20181109031633_create_purchasers.up.sql
worked just fine, but running the migration through 'migrate' produced
this error:
(details: Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'DELIMITER $$
CREATE TRIGGER purchasers_updated_at
BEFORE UPDATE
ON purchasers FO' at line 1)
Probably the same problem that 'migrant' had, but it just didn't give me
an error message.
After having to fiddle and mess around with different parts of the
migration for a while, it finally turned out that the `DELIMITER` seemed
to be the problem.
Got rid of it as well as the `BEGIN`/`END` that depended on it. Looks
like our timestamp updater still works even without `BEGIN`, though, so
that's good. When I had first written the code, I figured I should use
`BEGIN` because that was how it was written in a couple examples I had
seen.
|
|
Switched migration runners from https://crates.io/crates/migrant to
https://github.com/golang-migrate/migrate .
|
|
Wrap all `migrate` subcommands. Turns out we do need the `-path`
argument for the other commands. Doesn't appear to work correctly for
`create`, but for the others it works fine.
|
|
Wraps the `migrate` command (https://github.com/golang-migrate/migrate).
The command requires you to pass in a bunch of shell options that should
be constant for an application.
Encode those constants in this wrapper function. Now all you need to do
is run:
$ migrate migration_name
and the correct migrations will be generated.
The migrations get created in the current directory. I tried passing
-source file://migrations
,
-source file://./migrations
, and
-path migrations
but no combination seemed to put the generated migration files in the
migrations/ directory. Settled on moving the files manually in the
helper function.
|
|
A database table to hold purchaser information so we can re-generate
licenses in case purchasers lose their license keys.
Needed to use a trigger to update the `updated_at` field on `UPDATE` as
you can't do `ON UPDATE UTC_TIMESTAMP()` in the column definition
> You can not specify UTC_TIMESTAMP as default to specify automatic
> properties
(https://dba.stackexchange.com/questions/20217/mysql-set-utc-time-as-default-timestamp/21619#21619)
Weirdly, the trigger isn't working when applying the migration with
$ migrant apply
but it is working when running
$ mysql -u user dome_key < migrations/20181108234653_create-purchasers/up.sql
Not sure what's going on there, but 'migrant' appears to have trouble
realising there are errors coming back from MySQL and executes the
migrations regardless. It also doesn't print syntax error messages from
MySQL which is very inconvenient. Migrant seemed to be the most advanced
migration CLI on crates.io, and I was hoping to use a Rust program for
this, but for simplicity, I'm thinking I'll have to go with a different
migration runner. Considering https://github.com/golang-migrate/migrate.
|
|
Use 'migrant' for migrations (https://crates.io/crates/migrant).
TOML config file generated and modified from `migrant init`.
Database connection parameters should be passed in though environment
variables, hence the '.env.sample'.
|
|
|
|
|
|
Describe the program, high-level features.
Add styles:
* Fix leading
* Add list bullets that were removed by the reset
|
|
Increase contrast for improved readability.
|
|
Make them a bit easier to read.
|
|
|
|
Allows us to test the FastCGI script locally.
Thanks to this article for describing how to set up a local FastCGI
server with Lighttpd:
http://yaikhom.com/2014/12/18/handling-requests-c-fast-cgi-and-lighttpd.html
Found out how to get the current config directory using `var.CWD` from:
https://stackoverflow.com/questions/11702989/lighttpd-conf-document-root-as-directory-containing-config-file/12435777#12435777
|
|
Clearer explanation of what the image is.
|
|
* Add margins to the top & bottom of the page
* Align the logo relative to the content area, and move it left a bit,
outside of the column
|
|
|
|
|
|
There were a bunch of useless empty lines in the Hasp output. Would
rather have had a single newline between each CSS rule, but this is
close enough. At least it looks better.
|
|
For better organisation. Use the 'hasp' CSS preprocessor
(https://github.com/djanowski/hasp).
|
|
|
|
|
|
|
|
A rough setup for the website layout.
* Muted yellow background
* Logo set in Superclarendon by Typodermic
|
|
As these crates are only used in tests, move them to the
`dev-dependencies` section, and add `cfg(test)` to 'base64'.
|
|
In order to properly verify the signature, dictionary entries must be
serialized in sorted order. Seems simpler to put the onus on the caller
to ensure the entries can be sorted rather than having to deal with that
myself.
|
|
Hoping this is how to set up the verifier to verify the signature.
|
|
Not sure if this works yet as I haven't tested it, but it follows most
of the examples in various languages on:
https://paddle.com/docs/reference-verifying-webhooks/
Just need to add in the comparison to the input signature.
|
|
Replace the `ExactSizeIterator` with an `IntoIterator`, as I wasn't able
to pass in a `HashMap` with `ExactSizeIterator`.
While we lose the convenience of the `len()` method, it's easy enough to
just count the length of the iterator while we're serializing the
entries within it.
|
|
The expected value was generated with the following PHP code, some of
which was reproduced from the PHP example on
https://paddle.com/docs/reference-verifying-webhooks/ :
<?php
$fields = array(
'checkout_id' => '1234asdfjkl',
'currency' => 'USD',
'customer_name' => 'Senjougahara',
);
ksort($fields);
foreach($fields as $k => $v) {
if(!in_array(gettype($v), array('object', 'array'))) {
$fields[$k] = "$v";
}
}
$data = serialize($fields);
echo "Data: ", $data;
|
|
Should verify a Paddle webhook. This ensures that the request really
does come from Paddle.
In order to verify the request, we need to sign the serialized POST
parameters. And, weirdly, Paddle requires you to serialize parameters in
PHP's serialization format.
Using this Gist by 'drewmccormack' as a reference for the format:
https://gist.github.com/drewmccormack/a51b18ffeda8f596a11a8623481344d8
|
|
|
|
I prefer the capitalised key names, since this is a plist. Had gone with
lowercase before because it was the default.
|
|
When I updated the plist function tests, I discovered that the `data`
field for "Signature" was incorrect.
Turns out that the 'plist' crate takes it upon itself to base64 encode
the input Vec it gets:
https://github.com/ebarnard/rust-plist/blob/v0.3.0/src/xml/writer.rs#L171-L174
This meant I was double-base64 encoding the signature. To fix this, I
removed base64 encoding from the `sign()` method, and return the `[u8]`
array directly.
I thought this would be the end of it, but I ran into another problem
where my tests failed. It turns out that the 'plist' crate base64
encodes using the `base64::MIME` config, which wraps the base64-encoded
string to 76 characters and uses CRLF line endings. But my XML string
uses plain LF line endings, so I ended up with useless CRs in the
`<data>` tag.
To solve this, I ended up having to fork the 'plist' crate and change it
to use the `base64::STANDARD` config, which doesn't line wrap the base64
string. For now the fork is a local copy. I'll publish it when I'm ready
to publish the rest.
|
|
Extract this, which is the same in all the tests, to a function. Also
line wrap the public key strings.
|
|
Return an `Err` instead of unwrapping `Result`s.
* Add `std::string::FromUtf8Error` to `foreign_links` to support
`String::from_utf8` error result
* Add `InvalidLicenseData` when unwrapping the dictionary from
`plist_data`. The `as_dictionary()` method returns an `Option`, and if
we get `None` from that, our `plist()` function should consider that
an error because without this input data, it can't proceed.
|
|
Clean up the code from 70dcef3a47c11f921d8d742b2e1667e178a90b8f.
* Remove commented code
* Remove debug print statements
* Rename variables to make more sense
* Return the plist XML from the function
* Change `sign()` to take a `HashMap<String, String>` instead of
`HashMap<&str, &str>` so we don't have to convert the deserialized
`HashMap<String, String>` in `plist()`
|
|
Working version of a function that generates a license plist XML string
from input data.
Made a complete and utter mess of the code, and the two tests in this
commit aren't really tests, they're just a way of executing the
function.
The function takes a serializable data type, sends the data to the
`sign` function, and produces an XML plist string of the input data plus
a field for the signature.
In order to accomplish this, I ended up with a kind of roundabout data
manipulation.
1. We start with the user data type.
2. That then gets serialised to an XML plist using the 'plist' crate's
'serde' serializer.
3. The serialized XML is then deserialized to a `Plist` instance.
4. The dict is extracted from the `Plist` and fed to `sign()` get a
signature from the data.
5. We add the signature to the dict as a `Plist::Data`, in accordance
with the Aquatic Prime license file format.
6. The full dict including the signature entry is serialized to XML and
returned from the function.
I ended up with this circuitous manipulation because I wanted to be able
to accept an arbitrary struct as input data, and serializing it seemed
to be the easiest way to get a plist dict from it. But we can't just use
that serialized result, because it doesn't contain the signature.
Getting a `Plist` from the data seemed to be the right way to go, as the
signature entry needs to be a plist `data` type, so we can't just insert
it into a `<String, String>` `HashMap`.
Now that it works, I'll be cleaning all of this up in further commits.
|
|
This check was reproduced from Aquatic Prime's C signature generator:
https://github.com/bdrister/AquaticPrime/blob/master/Source/PHP/AquaticPrimeCLI.c#L46-L49
In the case of this library, we should return an error to take
advantage of being able to return a `Result`.
|
|
Gets rid of `unwrap`s and allows us to explicitly remove only "0x".
Still on Rust 1.28 for now. Will need to change this to
`trim_start_matches` for Rust v1.30.
|
|
Remove most `unwrap`s in the function and replace them with tries.
|
|
|
|
|
|
Keys in the input hash must be sorted alphabetically without considering
case in order to produce the correct signature. Learned this from the
Ruby library:
total = information.sort{|a,b| a[0].downcase <=> b[0].downcase || a[0] <=> b[0]}.map{|key,value| value}.join('')
|
|
|
|
The spaces at the ends of the lines are included in the string, but they
shouldn't be.
|
|
Learned from:
https://github.com/bdrister/AquaticPrime/blob/57c6f70/Source/Cocoa/AquaticPrime.m#L111-L114
that the "0x" prefix on the hex keys needs to be removed before using
the RSA library functions on it. Otherwise it doesn't work. Couldn't
figure that out from the other implementations because they use custom
conversion functions instead of OpenSSL's functions.
Previously, calling `BigNum::from_hex_str()` on the strings would yield
`0`, but now we get proper BigNums.
|
|
Take the data string we created and feed it through RSA encryption.
Basically following the steps used by the Aquatic Prime CLI program
written in C:
https://github.com/bdrister/AquaticPrime/blob/4f86666/Source/PHP/AquaticPrimeCLI.c
At first I tried using a `Signer`, but it looks like I'm supposed to be
encrypting the data instead. Unfortunately, I'm now getting this error
when calling `private_encrypt()`:
'failed to encrypt: ErrorStack([Error { code: 67526721, library: "rsa
routines", function: "RSA_EAY_PRIVATE_ENCRYPT", reason: "malloc
failure", file: "rsa_eay.c", line: 368 }])', libcore/result.rs:945:5
Not sure what the problem is yet.
|