Skip to content

Commit

Permalink
Finished implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
samdark committed Aug 7, 2015
1 parent 04976c8 commit 3078c5b
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 38 deletions.
11 changes: 7 additions & 4 deletions WebshellAsset.php → JqueryTerminalAsset.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<?php


namespace samdark\webshell;


use yii\web\AssetBundle;

class WebshellAsset extends AssetBundle
/**
* JqueryTerminalAsset is an asset bundle used to include JQueryTerminal into the page.
*
* @see http://terminal.jcubic.pl/
* @author Alexander Makarov <sam@rmcreative.ru>
*/
class JqueryTerminalAsset extends AssetBundle
{
public $sourcePath = '@bower/jquery.terminal';

Expand Down
89 changes: 88 additions & 1 deletion Module.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,96 @@
<?php
namespace samdark\webshell;

use Yii;
use yii\web\ForbiddenHttpException;

/**
* This is the main module class for the web shell module.
*
* To use web shell, include it as a module in the application configuration like the following:
*
* ~~~
* return [
* 'modules' => [
* 'webshell' => ['class' => 'samdark\webshell\Module'],
* ],
* ]
* ~~~
*
* With the above configuration, you will be able to access web shell in your browser using
* the URL `http://localhost/path/to/index.php?r=webshell`
*
* @author Alexander Makarov <sam@rmcreative.ru>
*/
class Module extends \yii\base\Module
{
public $exitUrl = null;
/**
* @inheritdoc
*/
public $controllerNamespace = 'samdark\webshell\controllers';

/**
* @var string console greetings
*/
public $greetings = 'Yii 2.0 web shell';

/**
* @var array URL to use for `quit` command. If not set, `quit` command will do nothing.
*/
public $quitUrl;

/**
* @var string path to `yii` script
*/
public $yiiScript = '@app/yii';

/**
* @var array the list of IPs that are allowed to access this module.
* Each array element represents a single IP filter which can be either an IP address
* or an address with wildcard (e.g. 192.168.0.*) to represent a network segment.
* The default value is `['127.0.0.1', '::1']`, which means the module can only be accessed
* by localhost.
*/
public $allowedIPs = ['127.0.0.1', '::1'];

/**
* @inheritdoc
*/
public function init()
{
parent::init();
set_time_limit(0);
}

/**
* @inheritdoc
*/
public function beforeAction($action)
{
if (!parent::beforeAction($action)) {
return false;
}

if (Yii::$app instanceof \yii\web\Application && !$this->checkAccess()) {
throw new ForbiddenHttpException('You are not allowed to access this page.');
}

return true;
}

/**
* @return boolean whether the module can be accessed by the current user
*/
protected function checkAccess()
{
$ip = Yii::$app->getRequest()->getUserIP();
foreach ($this->allowedIPs as $filter) {
if ($filter === '*' || $filter === $ip || (($pos = strpos($filter, '*')) !== false && !strncmp($ip, $filter, $pos))) {
return true;
}
}
Yii::warning('Access to web shell is denied due to IP address restriction. The requested IP is ' . $ip, __METHOD__);

return false;
}
}
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Yii 2.0 web shell
=================

A web shell that allows to run yii console commands and create your own commands.
Web shell allows to run `yii` console commands using a browser.

Installation
------------
Expand All @@ -11,18 +11,31 @@ The preferred way to install this extension is through [composer](http://getcomp
Either run

```
php composer.phar require --prefer-dist samdark/yii2-webshell "*"
php composer.phar require --prefer-dist samdark/yii2-webshell "~1.0"
```

or add

```
"samdark/yii2-webshell": "*"
"samdark/yii2-webshell": "~1.0"
```

to the require section of your `composer.json` file.


Usage
-----
Configuration
-------------

To use web shell, include it as a module in the application configuration like the following:

```php
return [
'modules' => [
'webshell' => ['class' => 'samdark\webshell\Module'],
],
]
```

With the above configuration, you will be able to access web shell in your browser using
the URL `http://localhost/path/to/index.php?r=webshell`

3 changes: 3 additions & 0 deletions assets/webshell.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background: black;
}
68 changes: 62 additions & 6 deletions controllers/DefaultController.php
Original file line number Diff line number Diff line change
@@ -1,24 +1,80 @@
<?php


namespace samdark\webshell\controllers;


use Yii;
use yii\helpers\Json;
use yii\helpers\Url;
use yii\web\Controller;
use yii\web\Response;

/**
* DefaultController
*
* @author Alexander Makarov <sam@rmcreative.ru>
*
* @property \samdark\webshell\Module $module
*/
class DefaultController extends Controller
{
/**
* @inheritdoc
*/
public function init()
{
Yii::$app->request->enableCsrfValidation = false;
parent::init();
}

/**
* Displays initial HTML markup
* @return string
*/
public function actionIndex()
{
$this->layout = 'shell';
return $this->render('index');
return $this->render('index', [
'quitUrl' => $this->module->quitUrl ? Url::toRoute($this->module->quitUrl) : null,
'greetings' => $this->module->greetings
]);
}

/**
* ./yii proxy
* RPC handler
* @return array
*/
public function actionYii()
public function actionRpc()
{
Yii::$app->response->format = Response::FORMAT_JSON;

$options = Json::decode(Yii::$app->request->getRawBody());

switch ($options['method']) {
case 'yii':
list ($status, $output) = $this->runConsole(implode(' ', $options['params']));
return ['result' => $output];
}
}

/**
* Runs console command
*
* @param string $command
*
* @return array [status, output]
*/
private function runConsole($command)
{
$cmd = Yii::getAlias($this->module->yiiScript) . ' ' . $command . ' 2>&1';

$handler = popen($cmd, 'r');
$output = '';
while (!feof($handler)) {
$output .= fgets($handler);
}

$output = trim($output);
$status = pclose($handler);

return [$status, $output];
}
}
62 changes: 45 additions & 17 deletions views/default/index.php
Original file line number Diff line number Diff line change
@@ -1,27 +1,55 @@
<?php
/** @var yii\web\View $this */
?>
<div id="webshell"></div>
/** @var $this yii\web\View */
/** @var $quitUrl string */
/** @var $greetings string */
use yii\helpers\Url;

\samdark\webshell\WebshellAsset::register($this);

$endpoint = Url::toRoute(['default/rpc']);

$this->title = $greetings;

<?php
$this->registerJs(
<<<JS
jQuery(function($, undefined) {
$('#webshell').terminal(function(command, term) {
if (command !== '') {
var result = window.eval(command);
if (result != undefined) {
term.echo(String(result));
jQuery(function($) {
$('#webshell').terminal(
function(command, term) {
if (command.indexOf('yii') === 0 || command.indexOf('yii') === 3) {
$.jrpc('{$endpoint}', 'yii', [command.replace(/^yii ?/, '')], function(json) {
term.echo(json.result);
});
} else if (command === 'help') {
term.echo('Available commands are:');
term.echo('');
term.echo("clear\tClear console");
term.echo('help\tThis help text');
term.echo('yii\tyii command');
term.echo('quit\tQuit web shell');
} else if (command === 'quit') {
var exitUrl = '{$quitUrl}';
if (exitUrl) {
term.echo('Bye!');
location.replace(exitUrl);
} else {
term.echo('There is no exit.');
}
} else {
term.echo('Unknown command.');
}
},
{
greetings: '$greetings',
name: 'yii2-webshell',
prompt: '$ '
}
}, {
greetings: 'Javascript Interpreter',
name: 'js_demo',
height: 200,
width: 450,
prompt: 'js> '});
);
$('html').on('keydown', function(){
$('#webshell').click();
});
});
JS
);
?>

<div id="webshell"></div>
23 changes: 18 additions & 5 deletions views/layouts/shell.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
<?php
/** @var $this yii\web\View */
/** @var $content string */
use yii\helpers\Html;
?>
<?php $this->beginPage() ?>
<!doctype html>
<html>
<html lang="<?= Yii::$app->language ?>">
<head>
<meta charset="<?php echo \Yii::$app->charset?>">
<title><?php echo $this->pageTitle?></title>
<meta charset="<?= Yii::$app->charset ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?= Html::csrfMetaTags() ?>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<body>
<?= $content?>
<?php $this->beginBody() ?>
<?= $content?>
<?php $this->endBody() ?>
</body>
</html>
</html>
<?php $this->endPage() ?>

0 comments on commit 3078c5b

Please sign in to comment.