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

Add custom S3 endpoint support #220

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 14 additions & 1 deletion auth-oauth/authentication.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ function bootstrap() {
UserAuthenticationBackend::register(
new GoogleClientAuthBackend($this->getConfig()));
}

# ----- Generic OAuth2 ---------------------
$generic = $config->get('generic-enabled');
if (in_array($generic, array('all', 'staff'))) {
require_once('generic-oauth2.php');
StaffAuthenticationBackend::register(
new GenericOAuth2StaffAuthBackend($this->getConfig()));
}
if (in_array($generic, array('all', 'client'))) {
require_once('generic-oauth2.php');
UserAuthenticationBackend::register(
new GenericOAuth2AuthBackend($this->getConfig()));
}
}
}

Expand All @@ -29,4 +42,4 @@ function bootstrap() {
$loader = new UniversalClassLoader_osTicket();
$loader->registerNamespaceFallbacks(array(
dirname(__file__).'/lib'));
$loader->register();
$loader->register();
34 changes: 33 additions & 1 deletion auth-oauth/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@ function getOptions() {
'configuration' => array('size'=>60, 'length'=>100),
)),
'g-enabled' => clone $modes,
'generic' => new SectionBreakField(array(
'label' => $__('Generic OAuth2'),
)),
'generic-servicename' => new TextboxField(array(
'label' => $__('Service name'),
'configuration' => array('size'=>60, 'length'=>200),
)),
'generic-client-id' => new TextboxField(array(
'label' => $__('Client ID'),
'configuration' => array('size'=>60, 'length'=>200),
)),
'generic-client-secret' => new TextboxField(array(
'label' => $__('Client Secret'),
'configuration' => array('size'=>60, 'length'=>200),
)),
'generic-authorize-url' => new TextboxField(array(
'label' => $__('Authorize URL'),
'configuration' => array('size'=>60, 'length'=>200),
)),
'generic-token-url' => new TextboxField(array(
'label' => $__('Token URL'),
'configuration' => array('size'=>60, 'length'=>200),
)),
'generic-userinfo-url' => new TextboxField(array(
'label' => $__('User JSON URL'),
'configuration' => array('size'=>60, 'length'=>200),
)),
'generic-scope' => new TextboxField(array(
'label' => $__('Scope'),
'configuration' => array('size'=>60, 'length'=>200),
)),
'generic-enabled' => clone $modes,
);
}
}
}
153 changes: 153 additions & 0 deletions auth-oauth/generic-oauth2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

use ohmy\Auth2;

class GenericOAuth {
var $config;
var $access_token;

function __construct($config) {
$this->config = $config;
}

function triggerAuth() {
$self = $this;
global $ost;
return Auth2::legs(3)
->set('id', $this->config->get('generic-client-id'))
->set('secret', $this->config->get('generic-client-secret'))
->set('redirect', rtrim($ost->getConfig()->getURL(), '/') . '/api/auth/ext')
->set('scope', $this->config->get('generic-scope'))

->authorize($this->config->get('generic-authorize-url'))
->access($this->config->get('generic-token-url'))
->finally(function($data) use ($self) {
$self->access_token = $data['access_token'];
});
}
}

class GenericOAuth2StaffAuthBackend extends ExternalStaffAuthenticationBackend {
static $id = "oauth";
static $name = "OAuth2";

static $sign_in_image_url = "";

var $config;

function __construct($config) {
$this->config = $config;
$this->oauth = new GenericOAuth($config);
}

function getServiceName() {
return $this->config->get('generic-servicename');
}

function signOn() {
// TODO: Check session for auth token
if (isset($_SESSION[':oauth']['profile']['nickname'])) {
if (($staff = StaffSession::lookup($_SESSION[':oauth']['profile']['nickname']))
&& $staff->getId()
) {
$staffobject = $staff;
}
} elseif (isset($_SESSION[':oauth']['profile']['email'])) {
if (($staff = StaffSession::lookup(array('email' => $_SESSION[':oauth']['profile']['email'])))
&& $staff->getId()
) {
$staffobject = $staff;
}
}
if (isset($staffobject)) {
if (!$staffobject instanceof StaffSession) {
// osTicket <= v1.9.7 or so
$staffobject = new StaffSession($user->getId());
}
return $staffobject;
} else {
$_SESSION['_staff']['auth']['msg'] = 'Have your administrator create a local account';
}
}

static function signOut($user) {
parent::signOut($user);
unset($_SESSION[':oauth']);
}


function triggerAuth() {
parent::triggerAuth();
$oauth = $this->oauth->triggerAuth();
$oauth->GET(
$this->config->get('generic-userinfo-url'), [], array('Authorization' => 'Bearer ' . $this->oauth->access_token))
->then(function($response) {
if (!($json = JsonDataParser::decode($response->text)))
return;
$_SESSION[':oauth']['profile'] = $json;
Http::redirect(ROOT_PATH . 'scp');
}
);
}
}

class GenericOAuth2AuthBackend extends ExternalUserAuthenticationBackend {
static $id = "oauth.client";
static $name = "OAuth2";

static $sign_in_image_url = "";

function __construct($config) {
$this->config = $config;
$this->oauth = new GenericOAuth($config);
}

function getServiceName() {
return $this->config->get('generic-servicename');
}

function supportsInteractiveAuthentication() {
return false;
}

function signOn() {
// TODO: Check session for auth token
if (isset($_SESSION[':oauth']['profile']['email'])) {
if (($acct = ClientAccount::lookupByUsername($_SESSION[':oauth']['profile']['email']))
&& $acct->getId()
&& ($client = new ClientSession(new EndUser($acct->getUser()))))
return $client;

elseif (isset($_SESSION[':oauth']['profile'])) {
// TODO: Prepare ClientCreateRequest
$profile = $_SESSION[':oauth']['profile'];
$info = array(
'email' => $profile['email'],
'name' => $profile['displayName'],
);
return new ClientCreateRequest($this, $info['email'], $info);
}
}
}

static function signOut($user) {
parent::signOut($user);
unset($_SESSION[':oauth']);
}

function triggerAuth() {
require_once INCLUDE_DIR . 'class.json.php';
parent::triggerAuth();
$oauth = $this->oauth->triggerAuth();
$oauth->GET(
$this->config->get('generic-userinfo-url'), [], array('Authorization' => 'Bearer ' . $this->oauth->access_token))
->then(function($response) {
if (!($json = JsonDataParser::decode($response->text)))
return;
$_SESSION[':oauth']['profile'] = $json;
Http::redirect(ROOT_PATH . 'login.php');
}
);
}
}

17 changes: 8 additions & 9 deletions auth-oauth/google.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ function triggerAuth() {
$google = $this->google->triggerAuth();
$google->GET(
"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token="
. $this->google->access_token)
. $this->google->access_token)
->then(function($response) {
require_once INCLUDE_DIR . 'class.json.php';
if ($json = JsonDataParser::decode($response->text))
$_SESSION[':oauth']['email'] = $json['email'];
Http::redirect(ROOT_PATH . 'scp');
}
);
);
}
}

Expand All @@ -101,8 +101,8 @@ function signOn() {
// TODO: Check session for auth token
if (isset($_SESSION[':oauth']['email'])) {
if (($acct = ClientAccount::lookupByUsername($_SESSION[':oauth']['email']))
&& $acct->getId()
&& ($client = new ClientSession(new EndUser($acct->getUser()))))
&& $acct->getId()
&& ($client = new ClientSession(new EndUser($acct->getUser()))))
return $client;

elseif (isset($_SESSION[':oauth']['profile'])) {
Expand All @@ -129,24 +129,23 @@ function triggerAuth() {
$token = $this->google->access_token;
$google->GET(
"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token="
. $token)
. $token)
->then(function($response) use ($google, $token) {
if (!($json = JsonDataParser::decode($response->text)))
return;
$_SESSION[':oauth']['email'] = $json['email'];
$google->GET(
"https://www.googleapis.com/plus/v1/people/me?access_token="
. $token)
. $token)
->then(function($response) {
if (!($json = JsonDataParser::decode($response->text)))
return;
$_SESSION[':oauth']['profile'] = $json;
Http::redirect(ROOT_PATH . 'login.php');
}
);
);
}
);
);
}
}


8 changes: 4 additions & 4 deletions auth-oauth/plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

return array(
'id' => 'auth:oath2', # notrans
'version' => '0.1',
'version' => '0.2',
'name' => /* trans */ 'Oauth2 Authentication and Lookup',
'author' => 'Jared Hancock',
'author' => 'Jared Hancock, Andreas Valder',
'description' => /* trans */ 'Provides a configurable authentication backend
for authenticating staff and clients using an OAUTH2 server
interface.',
'url' => 'http://www.osticket.com/plugins/auth/oauth',
'url' => 'https://github.com/osTicket/osTicket-plugins',
'plugin' => 'authentication.php:OauthAuthPlugin',
'requires' => array(
"ohmy/auth" => array(
Expand All @@ -20,4 +20,4 @@ interface.',
),
);

?>
?>
14 changes: 14 additions & 0 deletions storage-s3/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ function($x, $y, $n) { return $n != 1 ? $y : $x; },
function getOptions() {
list($__, $_N) = self::translate();
return array(
'endpoint' => new TextboxField(array(
'label' => $__('S3 Endpoint'),
'configuration' => array('size'=>40),
)),
'bucket' => new TextboxField(array(
'label' => $__('S3 Bucket'),
'configuration' => array('size'=>40),
Expand Down Expand Up @@ -55,6 +59,10 @@ function getOptions() {
),
'default' => '',
)),
'capath' => new TextboxField(array(
'label' => $__('CA Path'),
'configuration' => array('size'=>40),
)),

'access-info' => new SectionBreakField(array(
'label' => $__('Access Information'),
Expand All @@ -81,6 +89,12 @@ function pre_save(&$config, &$errors) {
?: Crypto::decrypt($this->get('secret-access-key'), SECRET_SALT,
$this->getNamespace()),
);
if ($config['endpoint'])
$credentials['endpoint'] = $config['endpoint'];

if ($config['capath'])
$credentials['ssl.certificate_authority'] = $config['capath'];

if ($config['aws-region'])
$credentials['region'] = $config['aws-region'];

Expand Down
19 changes: 15 additions & 4 deletions storage-s3/storage.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Aws\S3\Exception\S3Exception;
use Aws\S3\Exception\SignatureDoesNotMatchException;
use Aws\S3\Model\MultipartUpload\UploadBuilder;
use Aws\S3\S3Client;
Expand Down Expand Up @@ -33,8 +34,14 @@ function __construct($meta) {
'secret' => Crypto::decrypt(static::$config['secret-access-key'],
SECRET_SALT, static::getConfig()->getNamespace()),
);
if ($config['aws-region'])
$credentials['region'] = $config['aws-region'];
if (static::$config['aws-region'])
$credentials['region'] = static::$config['aws-region'];

if (static::$config['endpoint'])
$credentials['endpoint'] = static::$config['endpoint'];

if (static::$config['capath'])
$credentials['ssl.certificate_authority'] = static::$config['capath'];

$this->client = S3Client::factory($credentials);
}
Expand Down Expand Up @@ -87,6 +94,9 @@ function flush() {
return $this->upload($this->body);
}

/**
* @throws IOException
*/
function upload($filepath) {
if ($filepath instanceof EntityBody) {
$filepath->rewind();
Expand All @@ -107,6 +117,7 @@ function upload($filepath) {
if (isset($this->upload_hash))
$params['Content-MD5'] =
$this->upload_hash_final = hash_final($this->upload_hash);
echo $filepath;

$info = $this->client->upload(
static::$config['bucket'],
Expand All @@ -120,11 +131,11 @@ function upload($filepath) {
catch (S3Exception $e) {
throw new IOException('Unable to upload to S3: '.(string)$e);
}
return false;
}

// Support MD5 hash via the returned ETag header;
function getNativeHashAlgos() {
function getNativeHashAlgos(): array
{
return array('md5');
}

Expand Down