causefx %!s(int64=8) %!d(string=hai) anos
pai
achega
a50e9a6485

+ 9 - 1
api/config/default.php

@@ -8,5 +8,13 @@ return array(
     'headerColor' => '',
     'headerTextColor' => '',
     'lockScreen' => false,
-    'theme'=>'Organizr'
+    'theme'=>'Organizr',
+    'plexURL' => '',
+    'plexToken' => '',
+    'plexID' => '',
+    'tautulliURL' => '',
+    'ombiURL' => '',
+    'ssoPlex' => false,
+    'ssoOmbi' => false,
+    'ssoTautulli' => false
 );

+ 1 - 0
api/functions/api-functions.php

@@ -27,6 +27,7 @@ function login($array){
             if(createToken($result['username'],$result['email'],$result['image'],$result['group'],$result['group_id'],$GLOBALS['organizrHash'],$days)){
                 writeLoginLog($username, 'success');
                 writeLog('success', 'Login Function - A User has logged in', $username);
+                ssoCheck($username, $password);
                 return true;
             }
         }else{

+ 80 - 0
api/functions/organizr-functions.php

@@ -136,6 +136,86 @@ function organizrStatus(){
 	$status['php'] = phpversion();
     return $status;
 }
+function getSSO(){
+	return array(
+		array(
+			'type' => 'input',
+			'name' => 'plexToken',
+			'label' => 'Plex Token',
+			'value' => $GLOBALS['plexToken'],
+			'placeholder' => 'Use Get Token Button'
+		),
+		array(
+			'type' => 'button',
+			'label' => 'Get Plex Token',
+			'class' => 'popup-with-form getPlexTokenSSO',
+			'icon' => 'fa fa-paper-plane',
+			'text' => 'Retrieve',
+			'href' => '#sso-plex-token-form',
+			'attr' => 'data-effect="mfp-3d-unfold"'
+		),
+		array(
+			'type' => 'input',
+			'name' => 'plexID',
+			'label' => 'Plex Machine',
+			'value' => $GLOBALS['plexID'],
+			'placeholder' => 'Use Get Plex Machine Button'
+		),
+		array(
+			'type' => 'button',
+			'label' => 'Get Plex Machine',
+			'class' => 'popup-with-form getPlexMachineSSO',
+			'icon' => 'fa fa-paper-plane',
+			'text' => 'Retrieve',
+			'href' => '#sso-plex-machine-form',
+			'attr' => 'data-effect="mfp-3d-unfold"'
+		),
+		array(
+			'type' => 'html',
+			'label' => 'Plex Note',
+			'html' => '<span lang="en">Please make sure both Token and Machine are filled in</span>'
+		),
+		array(
+			'type' => 'switch',
+			'name' => 'ssoPlex',
+			'label' => 'Enable',
+			'value' => $GLOBALS['ssoPlex']
+		),
+		array(
+			'type' => 'input',
+			'name' => 'ombiURL',
+			'label' => 'Ombi URL',
+			'value' => $GLOBALS['ombiURL'],
+			'placeholder' => 'http(s)://hostname'
+		),
+		array(
+			'type' => 'switch',
+			'name' => 'ssoOmbi',
+			'label' => 'Enable',
+			'value' => $GLOBALS['ssoOmbi']
+		),
+		array(
+			'type' => 'input',
+			'name' => 'tautulliURL',
+			'label' => 'Tautulli URL',
+			'value' => $GLOBALS['tautulliURL'],
+			'placeholder' => 'http(s)://hostname'
+		),
+		array(
+			'type' => 'switch',
+			'name' => 'ssoTautulli',
+			'label' => 'Enable',
+			'value' => $GLOBALS['ssoTautulli']
+		)
+		/*array(
+			'type' => 'button',
+			'label' => 'Send Test',
+			'class' => 'phpmSendTestEmail',
+			'icon' => 'fa fa-paper-plane',
+			'text' => 'Send'
+		)*/
+	);
+}
 function loadAppearance(){
     $appearance = array();
     $appearance['logo'] = $GLOBALS['logo'];

+ 60 - 0
api/functions/sso-functions.php

@@ -0,0 +1,60 @@
+<?php
+
+function ssoCheck($username, $password, $token=null){
+	$test = '';
+	if($GLOBALS['ssoPlex']){
+		//coookie('set','mpt',$authSuccess['token'],7);
+	}
+	if($GLOBALS['ssoOmbi']){
+		$ombiToken = getOmbiToken($username, $password);
+		if($ombiToken){
+			coookie('set','Auth',$ombiToken,7, false);
+		}
+	}
+	if($GLOBALS['ssoTautulli']){
+		$tautulliToken = getTautulliToken($username, $password);
+		if($tautulliToken){
+			coookie('set','tuatulli_token_'.$tautulliToken['uuid'],$tautulliToken['token'],7, false);
+		}
+	}
+	return true;
+}
+function getOmbiToken($username, $password){
+	$url = $GLOBALS['ombiURL'].'/api/v1/Token';
+	$token = null;
+	$headers = array(
+		"Accept" => "application/json",
+		"Content-Type" => "application/json"
+	);
+	$data = array(
+		"username" => $username,
+		"password" => $password,
+		"rememberMe" => "true",
+	);
+	$options = (localURL($url)) ? array('verify' => false ) : array();
+	$response = Requests::post($url, $headers, json_encode($data), $options);
+	if($response->success){
+		$token = json_decode($response->body, true)['access_token'];
+	}
+	return ($token) ? $token : false;
+}
+function getTautulliToken($username, $password){
+	$url = $GLOBALS['tautulliURL'].'/auth/signin';
+	$token = null;
+	$headers = array(
+		"Accept" => "application/json",
+		"Content-Type" => "application/x-www-form-urlencoded"
+	);
+	$data = array(
+		"username" => $username,
+		"password" => $password,
+		"remember_me" => 1,
+	);
+	$options = (localURL($url)) ? array('verify' => false ) : array();
+	$response = Requests::post($url, $headers, $data, $options);
+	if($response->success){
+		$token['token'] = json_decode($response->body, true)['token'];
+		$token['uuid'] = json_decode($response->body, true)['uuid'];
+	}
+	return ($token) ? $token : false;
+}

+ 38 - 0
api/index.php

@@ -105,6 +105,25 @@ switch ($function) {
                 break;
         }
         break;
+    case 'v1_settings_settings_sso':
+        switch ($method) {
+            case 'GET':
+                if(qualifyRequest(1)){
+                    $result['status'] = 'success';
+                    $result['statusText'] = 'success';
+                    $result['data'] = $pageSettingsSettingsSSO;
+                }else{
+                    $result['status'] = 'error';
+                    $result['statusText'] = 'API/Token invalid or not set';
+                    $result['data'] = null;
+                }
+                break;
+            default:
+                $result['status'] = 'error';
+                $result['statusText'] = 'The function requested is not defined for method: '.$method;
+                break;
+        }
+        break;
     case 'v1_settings_customize_appearance':
         switch ($method) {
             case 'GET':
@@ -546,6 +565,25 @@ switch ($function) {
                 break;
         }
         break;
+    case 'v1_sso':
+        switch ($method) {
+            case 'GET':
+                if(qualifyRequest(1)){
+                    $result['status'] = 'success';
+                    $result['statusText'] = 'success';
+                    $result['data'] = getSSO();
+                }else{
+                    $result['status'] = 'error';
+                    $result['statusText'] = 'API/Token invalid or not set';
+                    $result['data'] = null;
+                }
+                break;
+            default:
+                $result['status'] = 'error';
+                $result['statusText'] = 'The function requested is not defined for method: '.$method;
+                break;
+        }
+        break;
     case 'v1_user_edit':
         switch ($method) {
             case 'POST':

+ 48 - 0
api/pages/settings-settings-sso.php

@@ -0,0 +1,48 @@
+<?php
+
+$pageSettingsSettingsSSO = '
+<script>
+	buildSSO();
+</script>
+<div class="panel bg-theme-dark panel-info">
+    <div class="panel-heading">
+		<span lang="en">Single Sign-On</span>
+	</div>
+    <div class="panel-wrapper collapse in" aria-expanded="true">
+        <div class="panel-body bg-theme-dark">
+            <form id="sso-form" class="form-horizontal" onsbumit="return false;"></form>
+        </div>
+    </div>
+</div>
+<form id="sso-plex-token-form" class="mfp-hide white-popup-block mfp-with-anim">
+    <h1 lang="en">Get Plex Token</h1>
+    <div class="panel ssoPlexTokenHeader">
+        <div class="panel-heading ssoPlexTokenMessage" lang="en">Enter Plex Details</div>
+    </div>
+    <fieldset style="border:0;">
+        <div class="form-group">
+            <label class="control-label" for="sso-plex-token-form-username" lang="en">Plex Username</label>
+            <input type="text" class="form-control" id="sso-plex-token-form-username" name="username" required="" autofocus>
+        </div>
+        <div class="form-group">
+            <label class="control-label" for="nsso-plex-token-form-password" lang="en">Plex Password</label>
+            <input type="password" class="form-control" id="sso-plex-token-form-password" name="password"  required="">
+        </div>
+    </fieldset>
+    <button class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right row b-none getSSOPlexToken" type="button"><span class="btn-label"><i class="fa fa-ticket"></i></span><span lang="en">Grab It</span></button>
+    <div class="clearfix"></div>
+</form>
+<form id="sso-plex-machine-form" class="mfp-hide white-popup-block mfp-with-anim">
+    <h1 lang="en">Get Plex Machine</h1>
+    <div class="panel ssoPlexMachineHeader">
+        <div class="panel-heading ssoPlexMachineMessage" lang="en"></div>
+    </div>
+    <fieldset style="border:0;">
+        <div class="form-group">
+            <label class="control-label" for="sso-plex-machine-form-machine" lang="en">Plex Machine</label>
+            <div class="ssoPlexMachineListing"></div>
+        </div>
+    </fieldset>
+    <div class="clearfix"></div>
+</form>
+';

+ 6 - 0
api/pages/settings.php

@@ -113,6 +113,8 @@ $pageSettings = '
                             </li>
                             <li onclick="changeSettingsMenu(\'Settings::System Settings::Main\')" role="presentation" class=""><a href="#settings-settings-main" aria-controls="home" role="tab" data-toggle="tab" aria-expanded="false"><span class="visible-xs"><i class="ti-settings"></i></span><span class="hidden-xs" lang="en"> Main</span></a>
                             </li>
+                            <li onclick="changeSettingsMenu(\'Settings::System Settings::SSO\');loadSettingsPage(\'api/?v1/settings/settings/sso\',\'#settings-settings-sso\',\'SSO\');" role="presentation" class=""><a href="#settings-settings-sso" aria-controls="home" role="tab" data-toggle="tab" aria-expanded="false"><span class="visible-xs"><i class="ti-receipt"></i></span><span class="hidden-xs" lang="en"> Single Sign-On</span></a>
+                            </li>
                             <li onclick="changeSettingsMenu(\'Settings::System Settings::Logs\');loadSettingsPage(\'api/?v1/settings/settings/logs\',\'#settings-settings-logs\',\'Log Viewer\');" role="presentation" class=""><a href="#settings-settings-logs" aria-controls="home" role="tab" data-toggle="tab" aria-expanded="false"><span class="visible-xs"><i class="ti-receipt"></i></span><span class="hidden-xs" lang="en"> Logs</span></a>
                             </li>
                             <li onclick="changeSettingsMenu(\'Settings::System Settings::Updates\')" role="presentation" class=""><a href="#settings-settings-updates" aria-controls="profile" role="tab" data-toggle="tab" aria-expanded="false"><span class="visible-xs"><i class="ti-package"></i></span> <span class="hidden-xs" lang="en">Updates</span></a>
@@ -126,6 +128,10 @@ $pageSettings = '
                                 <h2 lang="en">Main Settings</h2>
                                 <div class="clearfix"></div>
                             </div>
+                            <div role="tabpanel" class="tab-pane fade" id="settings-settings-sso">
+                                <h2 lang="en">Loading...</h2>
+                                <div class="clearfix"></div>
+                            </div>
                             <div role="tabpanel" class="tab-pane fade" id="settings-settings-logs">
                                 <h2 lang="en">Loading...</h2>
                                 <div class="clearfix"></div>

+ 0 - 1
api/plugins/js/php-mailer.js

@@ -44,7 +44,6 @@ function phpmBuildSettingsItems(array){
         });
         mailItems += '</div>';
     }
-
     return mailItems;
 }
 

+ 0 - 18
api/plugins/php-mailer.php

@@ -47,24 +47,6 @@ function phpmSendTestEmail(){
 }
 /* GET PHPMAILER SETTINGS */
 function phpmGetSettings(){
-	/*$like = 'PHPMAILER';
-	 return array_filter_key(configLazy(), function ($item) use ($like) {
-		if (stripos($item, $like) !== false) {
-			return true;
-		}
-		return false;
-	});*/
-	/*
-	'PHPMAILER-enabled' => false,
-    'PHPMAILER-smtpHost' => '',
-    'PHPMAILER-smtpHostPort' => '',
-    'PHPMAILER-smtpHostAuth' => true,
-    'PHPMAILER-smtpHostUsername' => '',
-    'PHPMAILER-smtpHostPassword' => '',
-    'PHPMAILER-smtpHostSenderName' => 'Organizr',
-    'PHPMAILER-smtpHostSenderEmail' => 'no-reply@Organizr.tld',
-    'PHPMAILER-smtpHostType' => 'tls'
-	*/
 	return array(
 		array(
 			'type' => 'input',

+ 116 - 1
js/custom.js

@@ -1086,7 +1086,122 @@ $(document).on('click', '.disablePlugin', function() {
     });
 
 });
-
+// SSO Option change
+$(document).on('change asColorPicker::close', '#sso-form :input', function(e) {
+    var input = $(this);
+    switch ($(this).attr('type')) {
+        case 'switch':
+        case 'checkbox':
+            var value = $(this).prop("checked") ? true : false;
+            break;
+        default:
+            var value = $(this).val();
+    }
+	var post = {
+        api:'api/?v1/update/config',
+        name:$(this).attr("name"),
+        type:$(this).attr("data-type"),
+        value:value,
+        messageTitle:'',
+        messageBody:'Updated Value for '+$(this).parent().parent().find('label').text(),
+        error:'Organizr Function: API Connection Failed'
+    };
+	var callbacks = $.Callbacks();
+    //callbacks.add( buildCustomizeAppearance );
+    settingsAPI(post,callbacks);
+    //disable button then renable
+    $('#sso-form :input').prop('disabled', 'true');
+    setTimeout(
+        function(){
+            $('#sso-form :input').prop('disabled', null);
+            input.emulateTab();
+        },
+        1500
+    );
+});
+$(document).on("click", ".getSSOPlexToken", function () {
+    $('.ssoPlexTokenMessage').text("Grabbing Token");
+    $('.ssoPlexTokenHeader').addClass('panel-info').removeClass('panel-warning').removeClass('panel-danger');
+    var plex_username = $('#sso-plex-token-form [name=username]').val().trim();
+    var plex_password = $('#sso-plex-token-form [name=password]').val().trim();
+    if ((plex_password !== '') && (plex_password !== '')) {
+        $.ajax({
+            type: 'POST',
+            headers: {
+                'X-Plex-Product':'Organizr',
+                'X-Plex-Version':'2.0',
+                'X-Plex-Client-Identifier':'01010101-10101010'
+            },
+            url: 'https://plex.tv/users/sign_in.json',
+            data: {
+                'user[login]': plex_username,
+                'user[password]': plex_password,
+                force: true
+            },
+            cache: false,
+            async: true,
+            complete: function(xhr, status) {
+                var result = $.parseJSON(xhr.responseText);
+                if (xhr.status === 201) {
+                    $('.ssoPlexTokenMessage').text(xhr.statusText);
+                    $('.ssoPlexTokenHeader').addClass('panel-success').removeClass('panel-info').removeClass('panel-warning').removeClass('panel-danger');
+                    $('#sso-form [name=plexToken]').val(result.user.authToken);
+                    $('#sso-form [name=plexToken]').change();
+                } else {
+                    $('.ssoPlexTokenMessage').text(xhr.statusText);
+                    $('.ssoPlexTokenHeader').addClass('panel-danger').removeClass('panel-info').removeClass('panel-warning');
+                }
+            }
+        });
+    } else {
+        $('.ssoPlexTokenMessage').text("Enter Username and Password");
+        $('.ssoPlexTokenHeader').addClass('panel-warning').removeClass('panel-info').removeClass('panel-danger');
+    }
+});
+$(document).on("click", ".getPlexMachineSSO", function () {
+    var plex_token = $('#sso-form [name=plexToken]').val().trim();
+    if (plex_token !== '') {
+        $('.ssoPlexMachineMessage').text("Grabbing List");
+        $('.ssoPlexMachineHeader').addClass('panel-info').removeClass('panel-warning').removeClass('panel-danger');
+        $.ajax({
+            type: 'GET',
+            headers: {
+                'X-Plex-Product':'Organizr',
+                'X-Plex-Version':'2.0',
+                'X-Plex-Client-Identifier':'01010101-10101010',
+                'X-Plex-Token':plex_token,
+            },
+            url: 'https://plex.tv/pms/servers.xml',
+            cache: false,
+            async: true,
+            complete: function(xhr, status) {
+                var result = $.parseXML(xhr.responseText);
+                if (xhr.status === 200) {
+                    $('.ssoPlexMachineMessage').text('Choose Plex Server');
+                    $('.ssoPlexMachineHeader').addClass('panel-success').removeClass('panel-info').removeClass('panel-warning');
+                    var machines = '<option lang="en">Choose Plex Machine</option>';
+                    $('Server', result).each(function(){
+                        var name = $(this).attr('name');
+                        var machine = $(this).attr('machineIdentifier');
+                        machines += '<option value="'+machine+'">'+name+'</option>';
+                    })
+                    var listing = `<select class="form-control" id="ssoPlexMachineSelector" data-type="select">`+machines+`</select>`;
+                    $('.ssoPlexMachineListing').html(listing);
+                } else {
+                    $('.ssoPlexTokenMessage').text(xhr.statusText);
+                    $('.ssoPlexTokenHeader').addClass('panel-danger').removeClass('panel-info').removeClass('panel-warning');
+                }
+            }
+        });
+    } else {
+        $('.ssoPlexMachineMessage').text("Plex Token Needed");
+        $('.ssoPlexMachineHeader').addClass('panel-warning').removeClass('panel-info').removeClass('panel-danger');
+    }
+});
+$(document).on('change', '#ssoPlexMachineSelector', function(e) {
+    $('#sso-form [name=plexID]').val($(this).val());
+    $('#sso-form [name=plexID]').change();
+});
 $(document).on("click", ".closeErrorPage", function () {
     $('.error-page').html('');
     $('.error-page').fadeOut();

+ 57 - 6
js/functions.js

@@ -504,27 +504,35 @@ function buildFormItem(item){
 	var extraClass = (item.class) ? ' '+item.class : '';
 	var icon = (item.icon) ? ' '+item.icon : '';
 	var text = (item.text) ? ' '+item.text : '';
+	var attr = (item.attr) ? ' '+item.attr : '';
 	var disabled = (item.disabled) ? ' disabled' : '';
+	var href = (item.href) ? ' href="'+item.href+'"' : '';
 	//+tof(item.value,'c')+`
 	switch (item.type) {
 		case 'input':
-			return '<input data-changed="false" lang=en" type="text" class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+' />';
+			return '<input data-changed="false" lang=en" type="text" class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+attr+' />';
 			break;
 		case 'password':
-			return '<input data-changed="false" lang=en" type="password" class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+' autocomplete="new-password" />';
+			return '<input data-changed="false" lang=en" type="password" class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+attr+' autocomplete="new-password" />';
 			break;
 		case 'hidden':
-			return '<input data-changed="false" lang=en" type="hidden" class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+' />';
+			return '<input data-changed="false" lang=en" type="hidden" class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+attr+' />';
 			break;
 		case 'select':
-			return '<select class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+'>'+selectOptions(item.options, item.value)+'</select>';
+			return '<select class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+attr+'>'+selectOptions(item.options, item.value)+'</select>';
 			break;
 		case 'switch':
 		case 'checkbox':
-			return '<input data-changed="false" type="checkbox" class="js-switch'+extraClass+'" data-size="small" data-color="#99d683" data-secondary-color="#f96262"'+name+value+tof(item.value,'c')+id+disabled+type+' /><input data-changed="false" type="hidden"'+name+'value="false">';
+			return '<input data-changed="false" type="checkbox" class="js-switch'+extraClass+'" data-size="small" data-color="#99d683" data-secondary-color="#f96262"'+name+value+tof(item.value,'c')+id+disabled+type+attr+' /><input data-changed="false" type="hidden"'+name+'value="false">';
 			break;
 		case 'button':
-			return '<button class="btn btn-sm btn-success btn-rounded waves-effect waves-light b-none'+extraClass+'" type="button"><span class="btn-label"><i class="'+icon+'"></i></span><span lang="en">'+text+'</span></button>';
+			return '<button class="btn btn-sm btn-success btn-rounded waves-effect waves-light b-none'+extraClass+'" '+href+attr+'type="button"><span class="btn-label"><i class="'+icon+'"></i></span><span lang="en">'+text+'</span></button>';
+			break;
+		case 'blank':
+			return '';
+			break;
+		case 'html':
+			return item.html;
 			break;
 		default:
 			return false;
@@ -655,6 +663,38 @@ function buildCustomizeAppearanceItem(array){
 
 	return customizeItems;
 }
+function buildSSOItem(array){
+    if (Array.isArray(array)) {
+		var count = 0;
+        var preRow = `
+            <!-- FORM GROUP -->
+            <h3 class="box-title" lang="en">SSO Settings</h3>
+            <hr class="m-t-0 m-b-40">
+            <div class="row">
+        `;
+        var ssoItems = preRow;
+        $.each(array, function(i,v) {
+			count++;
+
+			if(count%2 !== 0 ){ ssoItems += '<div class="row start">'; };
+            ssoItems += `
+                <!-- INPUT BOX -->
+                <div class="col-md-6 p-b-10">
+                    <div class="form-group">
+                        <label class="control-label col-md-3" lang="en">`+v.label+`</label>
+                        <div class="col-md-9">
+                            `+buildFormItem(v)+`
+                        </div>
+                    </div>
+                </div>
+                <!--/ INPUT BOX -->
+            `;
+			if(count%2 == 0 ){ ssoItems += '</div><!--end-->'; };
+        });
+        ssoItems += '</div>';
+    }
+    return ssoItems;
+}
 function buildImageManagerViewItem(array){
 	var imageListing = '';
 	if (Array.isArray(array)) {
@@ -706,6 +746,17 @@ function buildCustomizeAppearance(){
 	});
 	ajaxloader();
 }
+function buildSSO(){
+	ajaxloader(".content-wrap","in");
+	organizrAPI('GET','api/?v1/sso').success(function(data) {
+		var response = JSON.parse(data);
+		$('#sso-form').html(buildSSOItem(response.data));
+		;
+	}).fail(function(xhr) {
+		console.error("Organizr Function: API Connection Failed");
+	});
+	ajaxloader();
+}
 function buildUserManagement(){
 	ajaxloader(".content-wrap","in");
 	organizrAPI('GET','api/?v1/user/list').success(function(data) {