Skip to content

Commit

Permalink
Merge pull request #14 from mdsol/feature/preparing_opensource
Browse files Browse the repository at this point in the history
Preparing for open-sourcing
  • Loading branch information
jcarres-mdsol authored May 20, 2024
2 parents ec66e61 + 706ac9d commit 9c5e129
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 49 deletions.
5 changes: 0 additions & 5 deletions .cargo/config.toml

This file was deleted.

8 changes: 3 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish to Artifactory
name: Publish

on:
push:
Expand All @@ -22,8 +22,6 @@ jobs:
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Publish to Artifactory
run: |
cargo publish --registry artifactory --allow-dirty --token "Bearer $PLATFORM_LIBRARIES_ARTIFACTORY_TOKEN"
- run: cargo publish
env:
PLATFORM_LIBRARIES_ARTIFACTORY_TOKEN: "${{secrets.PLATFORM_LIBRARIES_ARTIFACTORY_TOKEN}}"
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
48 changes: 21 additions & 27 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,36 @@
# Contributing

## Requirements
<!--

List out any software requirements for running this project locally, for example:
* Ruby 3.0
* Postgresql 11.2
* Cassandra 9.0
For bonus points, explain how to install each.
-->
* Rust

## Installation
<!--
Use this section to list out steps that are needed to setup this project locally. For example:
* Cloning the repository
* Running `bundle install`

-->
This repo contains the submodule `mauth-protocol-test-suite` so requires a flag when initially cloning in order to clone and init submodules.

## Usage
<!--
```
git clone --recurse-submodules git@github.com:mdsol/mauth-core.git
```

Use this section to explain how to run the app after it's installed.
## General Information

-->
* Check out the latest develop to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
* Fork the project
* Start a feature/bugfix branch
* Commit and push until you are happy with your contribution
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.

## Testing
<!--
## Running Tests

Use this section to explain how to run the tests
```
cargo test --verbose
```

-->
## Running Benchmark

## Branches & pull requests
We use the git-flow branch strategy. Features should be based off the `develop` branch and merged using GitHub pull requests.
If you make changes which could affect performance, please run the benchmark before and after the change as a sanity check.

```
cargo bench
```
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@
name = "mauth-core"
version = "0.4.1"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
authors = ["Medidata Solutions <support@mdsol.com>"]
description = "Generate and verify Medidata MAuth protocol signatures"
readme = "README.md"
license = "MIT"
homepage = "https://github.com/mdsol/mauth-core"
repository = "https://github.com/mdsol/mauth-core"
keywords = ["security", "authentication"]
categories = ["authentication"]

[dependencies]
anyhow = "1"
Expand Down
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,29 @@ A library to generate and verify MAuth signatures

## Usage

Add the following to your `Cargo.toml`:

```toml
[dependencies]
mauth-core = "0.4"
```

Here is an example of generating and verifying a signature:

```rust
use mauth_core::signer::Signer;
use mauth_core::verifier::Verifier;

let mauth_version = 2

let signer = Signer::new(app_uuid, private_key_data)?;
let signiture = signer.sign_string(mauth_version, verb, path, query, body, timestamp)?

let verifier = Verifier::new(app_uuid, public_key_data)?;
let is_valid = verifier.verify_signature(mauth_version, verb, path, query, body, timestamp, signature)?;
```

You can find an example of binding MAuth Core to Ruby [here](./doc/binding_to_ruby.md).

## Contributing
See [CONTRIBUTING](CONTRIBUTING.md).

## Contact
See the [factbook](factbook.yaml).
191 changes: 191 additions & 0 deletions doc/binding_to_ruby.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Example of binding MAuth Core to Ruby

This document is an example of how to bind MAuth Core in [MAuth-Client ruby](https://github.com/mdsol/mauth-client-ruby).

## Required Ruby Gems

The following Ruby gems are used to bind:
- [Magnus](https://github.com/matsadler/magnus)
- [rb-sys](https://github.com/oxidize-rb/rb-sys)
- [rake-compiler](https://github.com/rake-compiler/rake-compiler)

## Modifying MAuth-Client ruby

Add the following gems to [mauth-client.gemspec](https://github.com/mdsol/mauth-client-ruby/blob/master/mauth-client.gemspec):

```
spec.add_dependency "rake-compiler"
spec.add_dependency "rb_sys"
```

And update [Rakefile](https://github.com/mdsol/mauth-client-ruby/blob/master/Rakefile) to add the following extension task:

```
require "rake/extensiontask"
task build: :compile
Rake::ExtensionTask.new("mauth_core_binder") do |ext|
ext.lib_dir = "lib/mauth"
ext.source_pattern = "*.{rs,toml}"
end
task default: %i[compile spec]
```

## Generate extension library

Prepare an extension directory and create a new Cargo package to bind mauth-core:

```sh
mkdir -p ext/mauth_core_binder
cd ext/mauth_core_binder

cargo init . --lib
cargo add rb-sys rb-allocator
cargo add magnus --features rb-sys-interop
```

Set the crate-type attribute to `cdylib` in Cargo.toml:

```
[lib]
crate-type = ["cdylib"]
```

Add `ext/mauth_core_binder/extconf.rb` file:

```ruby
require "mkmf"
require "rb_sys/mkmf"

create_rust_makefile("mauth_core_binder/mauth_core_binder")
```

At this point, you are ready to compile the rust extension by calling:
```
bundle exec rake compile
```

Add the `mauth-core` crate to Cargo.toml:

```
[dependencies]
mauth-core = "0.4"
```

Then, add some Rust code to the `lib.rs` file to call mauth-core:

```rust
use magnus::{define_class, exception, function, method, prelude::*, Error};
use rb_allocator::ruby_global_allocator;

use mauth_core::signer::Signer;
use mauth_core::verifier::Verifier;

ruby_global_allocator!();

#[magnus::wrap(class = "MAuthCore")]
struct MAuthCore {
signer: Signer,
}

impl MAuthCore {
fn new(app_uuid: String, private_key_data: String) -> Self {
let signer =
Signer::new(app_uuid, private_key_data).expect("Failed to initialize MAuthCore");

Self { signer }
}

fn sign_string(
&self,
version: u8,
verb: String,
path: String,
query: String,
body: magnus::Value,
timestamp: String,
) -> Result<String, magnus::Error> {

let body = magnus::RString::from_value(body).ok_or_else(|| Error::new(
exception::standard_error(),
"expected string",
))?;

let body_as_slice;
unsafe {
body_as_slice = body.as_slice();
}

self.signer
.sign_string(version, verb, path, query, body_as_slice, timestamp)
.map_err(|err| {
Error::new(
exception::standard_error(),
format!("Failed to generate sigunatures: {:?}", err),
)
})
}

fn verify_signature(
&self,
app_uuid: String,
public_key_data: String,
version: u8,
verb: String,
path: String,
query: String,
body: magnus::Value,
timestamp: String,
signature: String,
) -> Result<bool, magnus::Error> {

let body = magnus::RString::from_value(body).ok_or_else(|| Error::new(
exception::standard_error(),
"expected string",
))?;

let body_as_slice;
unsafe {
body_as_slice = body.as_slice();
}

match Verifier::new(app_uuid, public_key_data) {
Ok(verifier) => verifier
.verify_signature(version, verb, path, query, body, timestamp, signature)
.map_err(|err| {
Error::new(
exception::standard_error(),
format!("Failed to verify sigunatures: {:?}", err),
)
}),
Err(err) => Err(Error::new(
exception::standard_error(),
format!("Failed to initialize verifier: {:?}", err),
)),
}
}
}

#[magnus::init]
fn init() -> Result<(), Error> {
let class = define_class("MAuthCore", Default::default())?;
class.define_singleton_method("new", function!(MAuthCore::new, 2))?;
class.define_method("sign_string", method!(MAuthCore::sign_string, 6))?;
class.define_method("verify_signature", method!(MAuthCore::verify_signature, 9))?;

Ok(())
}
```

By adding the `#[magnus::wrap(class = "MAuthCore")]` annotation, the MAuthCore struct is wrapped in a Ruby object and it is callable from Ruby.

Using the `#[magnus::init]` attribute to mark the init function so it can be correctly exposed to Ruby.

Now you can call `mauth-core` from Ruby code by doing this:

```ruby
mauth_core = MAuthCore.new(app_uuid, public_key_data)
mauth_core.sign_string(mauth_version, verb, path, query, body, timestamp)
```
7 changes: 0 additions & 7 deletions factbook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@ metadata:
teams:
- name: Architecture Enablement
number: 119
email: ae@mdsol.com
people:
- role: technical owner
email: ykitamura@mdsol.com
channels:
- url: https://mdsol.slack.com/messages/ae
automated_messaging: false
role: slack
annotations:
jira: https://jira.mdsol.com/secure/RapidBoard.jspa?rapidView=3118

0 comments on commit 9c5e129

Please sign in to comment.