mirror of
https://github.com/hak5/nano-tetra-modules.git
synced 2025-10-29 16:58:09 +00:00
sud0nick: Resolve merge conflicts for PR #50.
This commit is contained in:
commit
06b4d28f59
@ -69,12 +69,21 @@ class Papers extends Module
|
||||
case 'buildCert':
|
||||
$this->buildCert($this->request->parameters);
|
||||
break;
|
||||
case 'encryptKey':
|
||||
$this->respond($this->encryptKey($this->request->keyName, $this->request->keyType, $this->request->keyAlgo, $this->request->keyPass));
|
||||
break;
|
||||
case 'decryptKey':
|
||||
$this->respond($this->decryptKey($this->request->keyName, $this->request->keyType, $this->request->keyPass));
|
||||
break;
|
||||
case 'genSSHKeys':
|
||||
$this->genSSHKeys($this->request->parameters);
|
||||
break;
|
||||
case 'loadCertificates':
|
||||
$this->loadCertificates();
|
||||
break;
|
||||
case 'loadCertProps':
|
||||
$this->loadCertificateProperties($this->request->certName);
|
||||
break;
|
||||
case 'downloadKeys':
|
||||
$this->downloadKeys($this->request->parameters->name, $this->request->parameters->type);
|
||||
break;
|
||||
@ -288,6 +297,40 @@ class Papers extends Module
|
||||
$this->respond(true, "Keys created successfully!");
|
||||
}
|
||||
|
||||
private function encryptKey($keyName, $keyType, $algo, $pass) {
|
||||
$retData = array();
|
||||
$argString = "encryptKeys.sh --encrypt -k " . $keyName . " -a " . $algo . " -p " . $pass;
|
||||
|
||||
if ($keyType == "SSH") {
|
||||
$argString .= " --ssh";
|
||||
}
|
||||
|
||||
exec(__SCRIPTS__ . $argString, $retData);
|
||||
$res = implode("\n", $retData);
|
||||
if ($res != "Complete") {
|
||||
$this->logError("Key Encryption Error", "The following error occurred:\n\n" . $res);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function decryptKey($keyName, $keyType, $pass) {
|
||||
$retData = array();
|
||||
$argString = "decryptKeys.sh -k " . $keyName . " -p " . $pass;
|
||||
|
||||
if ($keyType == "SSH") {
|
||||
$argString .= " --ssh";
|
||||
}
|
||||
|
||||
exec(__SCRIPTS__ . $argString, $retData);
|
||||
$res = implode("\n", $retData);
|
||||
if ($res != "Complete") {
|
||||
$this->logError("Key Decryption Error", "The following error occurred:\n\n" . $res);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Generates an OpenSSL config file based on the passed in requirements ($req)
|
||||
and returns the path to the file.
|
||||
@ -327,6 +370,29 @@ class Papers extends Module
|
||||
$this->respond(true,null,$certs);
|
||||
}
|
||||
|
||||
private function loadCertificateProperties($cert) {
|
||||
$retData = array();
|
||||
$res = [];
|
||||
|
||||
exec(__SCRIPTS__ . "getCertInfo.sh -k " . $cert, $retData);
|
||||
if (count($retData) == 0) {
|
||||
$this->respond(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a mapping of the values that can be passed back to the front end
|
||||
foreach ($retData as $line) {
|
||||
$parts = explode("=", $line, 2);
|
||||
$key = $parts[0];
|
||||
$val = $parts[1];
|
||||
$res[$key] = $val;
|
||||
}
|
||||
|
||||
// Return success and the contents of the tmp file
|
||||
$this->respond(true, null, $res);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getKeys($dir) {
|
||||
$keyType = ($dir == __SSLSTORE__) ? "TLS/SSL" : "SSH";
|
||||
$keys = scandir($dir);
|
||||
@ -603,7 +669,17 @@ class Papers extends Module
|
||||
fclose($fh);
|
||||
}
|
||||
private function retrieveLog($logname, $type) {
|
||||
$dir = ($type == "error") ? __LOGS__ : ($type == "help") ? __HELPFILES__ : __CHANGELOGS__;
|
||||
switch($type) {
|
||||
case "error":
|
||||
$dir = __LOGS__;
|
||||
break;
|
||||
case "help":
|
||||
$dir = __HELPFILES__;
|
||||
break;
|
||||
default:
|
||||
$dir = __CHANGELOGS__;
|
||||
break;
|
||||
}
|
||||
$data = file_get_contents($dir . $logname);
|
||||
if (!$data) {
|
||||
$this->respond(false);
|
||||
|
||||
5
Papers/includes/changelog/Version 1.6
Normal file
5
Papers/includes/changelog/Version 1.6
Normal file
@ -0,0 +1,5 @@
|
||||
December 28, 2018<br /></br >
|
||||
- Added feature to encrypt/decrypt existing private keys<br />
|
||||
- Added feature to view certificate properties in the web console<br />
|
||||
- Fixed a bug that prevented error logs from loading in the web console<br />
|
||||
- Removed null alert when enabling SSH keys<br />
|
||||
@ -1,5 +1,5 @@
|
||||
<strong>Encrypted</strong><br />
|
||||
Displays if the private key (.pem) is encrypted. Does not include encryption on .pfx containers.
|
||||
Displays if the private key is encrypted. Does not include encryption on .pfx containers.
|
||||
<br /><br />
|
||||
|
||||
<strong>PineSSL / PineSSH</strong><br />
|
||||
@ -13,6 +13,15 @@ For SSH key pairs this button has two modes (the state does not change for TLS/S
|
||||
</ul>
|
||||
<br />
|
||||
|
||||
<strong>Encrypt / Decrypt</strong><br />
|
||||
If this icon is highlighted green the private key is encrypted. Click the icon and enter the key's password to decrypt it.<br />
|
||||
If the icon is not highlighted it is not encrypted. Click the icon, select an algorithm, and enter a password to encrypt it.
|
||||
<br /><br />
|
||||
|
||||
<strong>View Certificate</strong><br />
|
||||
View the human readable properties of the certificate.
|
||||
<br /><br />
|
||||
|
||||
<strong>Download</strong><br />
|
||||
Packages the keys in the selected row into a zip archive and downloads it.
|
||||
<br /><br />
|
||||
|
||||
BIN
Papers/includes/icons/glyphicons-28-search.png
Normal file
BIN
Papers/includes/icons/glyphicons-28-search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
72
Papers/includes/scripts/decryptKeys.sh
Normal file
72
Papers/includes/scripts/decryptKeys.sh
Normal file
@ -0,0 +1,72 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Author: sud0nick
|
||||
# Date: Dec 2018
|
||||
|
||||
# Location of SSL keys
|
||||
ssl_store="/pineapple/modules/Papers/includes/ssl/";
|
||||
ssh_store="/pineapple/modules/Papers/includes/ssh/";
|
||||
|
||||
help() {
|
||||
echo "Decryption script for OpenSSL keys";
|
||||
echo "Usage: ./decryptKeys.sh <opts>";
|
||||
echo "Use './decryptKeys.sh --examples' to see example commands";
|
||||
echo '';
|
||||
echo 'NOTE:';
|
||||
echo "Current SSL store is at $ssl_store";
|
||||
echo '';
|
||||
echo 'Parameters:';
|
||||
echo '';
|
||||
echo -e '\t-k:\tName of key to be decrypted';
|
||||
echo -e '\t-p:\tPassword to use to unlock the key';
|
||||
echo -e '\t--ssh:\tThe key to encrypt is in the SSH store';
|
||||
echo -e '\t--help:\tDisplays this help info';
|
||||
echo '';
|
||||
}
|
||||
|
||||
examples() {
|
||||
echo '';
|
||||
echo 'Examples:';
|
||||
echo 'Decrypt private key:';
|
||||
echo './decryptKeys.sh -k keyName -p password';
|
||||
echo '';
|
||||
echo '';
|
||||
}
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
help;
|
||||
exit;
|
||||
fi
|
||||
|
||||
while [ "$#" -gt 0 ]
|
||||
do
|
||||
|
||||
if [[ "$1" == "--examples" ]]; then
|
||||
examples;
|
||||
exit;
|
||||
fi
|
||||
if [[ "$1" == "--ssh" ]]; then
|
||||
ssl_store=$ssh_store;
|
||||
fi
|
||||
if [[ "$1" == "--help" ]]; then
|
||||
help;
|
||||
exit;
|
||||
fi
|
||||
if [[ "$1" == "-k" ]]; then
|
||||
KEY="$2";
|
||||
fi
|
||||
if [[ "$1" == "-p" ]]; then
|
||||
PASS="$2";
|
||||
fi
|
||||
|
||||
shift
|
||||
done;
|
||||
|
||||
# Generate a password on the private key
|
||||
openssl rsa -in $ssl_store$KEY.key -out $ssl_store$KEY.key -passin pass:"$PASS" 2>/dev/null;
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "Bad Password";
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo "Complete"
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
# Location of SSL keys
|
||||
ssl_store="/pineapple/modules/Papers/includes/ssl/";
|
||||
ssh_store="/pineapple/modules/Papers/includes/ssh/";
|
||||
|
||||
help() {
|
||||
echo "Encryption/Export script for OpenSSL certificates";
|
||||
@ -21,6 +22,7 @@ help() {
|
||||
echo 'Encryption Options:';
|
||||
echo '';
|
||||
echo -e '\t--encrypt:\tMust be supplied to encrypt keys';
|
||||
echo -e '\t--ssh:\tThe key to encrypt is in the SSH store';
|
||||
echo -e '\t-a:\t\tAlgorithm to use for key encryption (aes256, 3des, camellia256, etc)';
|
||||
echo -e '\t-p:\t\tPassword to use for encryption';
|
||||
echo '';
|
||||
@ -66,8 +68,11 @@ fi
|
||||
if [[ "$1" == "--encrypt" ]]; then
|
||||
ENCRYPT_KEYS=true;
|
||||
fi
|
||||
if [[ "$1" == "--ssh" ]]; then
|
||||
ssl_store=$ssh_store;
|
||||
fi
|
||||
if [[ "$1" == "-a" ]]; then
|
||||
ALGO="$2";
|
||||
ALGO="$2";
|
||||
fi
|
||||
if [[ "$1" == "-k" ]]; then
|
||||
KEY="$2";
|
||||
|
||||
51
Papers/includes/scripts/getCertInfo.sh
Normal file
51
Papers/includes/scripts/getCertInfo.sh
Normal file
@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Author: sud0nick
|
||||
# Date: Dec 2018
|
||||
|
||||
# Location of SSL keys
|
||||
ssl_store="/pineapple/modules/Papers/includes/ssl/";
|
||||
|
||||
help() {
|
||||
echo "Get certificate properties via OpenSSL";
|
||||
echo "Usage: ./getCertInfo.sh <opts>";
|
||||
echo '';
|
||||
echo 'NOTE:';
|
||||
echo "Current SSL store is at $ssl_store";
|
||||
echo '';
|
||||
echo 'Parameters:';
|
||||
echo '';
|
||||
echo -e '\t-k:\tKey from which to retrieve properties';
|
||||
echo '';
|
||||
}
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
help;
|
||||
exit;
|
||||
fi
|
||||
|
||||
while [ "$#" -gt 0 ]
|
||||
do
|
||||
|
||||
if [[ "$1" == "-k" ]]; then
|
||||
KEY="$ssl_store$2.cer";
|
||||
fi
|
||||
|
||||
shift
|
||||
done;
|
||||
|
||||
ISSUER=$(openssl x509 -in $KEY -noout -issuer | sed 's/^[^=]*=//g');
|
||||
FINGERPRINT=$(openssl x509 -in $KEY -noout -fingerprint | sed 's/^[^=]*=//g');
|
||||
SUBJECT=$(openssl x509 -in $KEY -noout -subject | sed 's/^[^=]*=//g');
|
||||
START_DATE=$(openssl x509 -in $KEY -noout -startdate | sed 's/^[^=]*=//g');
|
||||
END_DATE=$(openssl x509 -in $KEY -noout -enddate | sed 's/^[^=]*=//g');
|
||||
SERIAL=$(openssl x509 -in $KEY -noout -serial | sed 's/^[^=]*=//g');
|
||||
ALT_NAMES=$(openssl x509 -in $KEY -noout -text | grep DNS | sed 's/^[^:]*://g');
|
||||
|
||||
echo "issuer=$ISSUER";
|
||||
echo "fingerprint=$FINGERPRINT";
|
||||
echo "subject=$SUBJECT";
|
||||
echo "start=$START_DATE";
|
||||
echo "end=$END_DATE";
|
||||
echo "serial=$SERIAL";
|
||||
echo "dns=$ALT_NAMES";
|
||||
@ -34,6 +34,13 @@ registerController('PapersController', ['$api', '$scope', '$sce', '$http', funct
|
||||
$scope.dependsProcessing = false;
|
||||
$scope.selectedFiles = [];
|
||||
$scope.uploading = false;
|
||||
$scope.selectedKey = '';
|
||||
$scope.certDecryptPassword = '';
|
||||
$scope.encrypting = false;
|
||||
$scope.decrypting = false;
|
||||
$scope.viewCert = '';
|
||||
$scope.selectedCert = '';
|
||||
$scope.loadingCert = false;
|
||||
|
||||
$scope.checkDepends = (function(){
|
||||
$api.request({
|
||||
@ -159,6 +166,7 @@ registerController('PapersController', ['$api', '$scope', '$sce', '$http', funct
|
||||
action: 'buildCert',
|
||||
parameters: params
|
||||
},function(response) {
|
||||
$scope.certKeyName = '';
|
||||
$scope.getLogs();
|
||||
$scope.showBuildThrobber = false;
|
||||
$scope.loadCertificates();
|
||||
@ -166,6 +174,94 @@ registerController('PapersController', ['$api', '$scope', '$sce', '$http', funct
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.loadCertProps = (function(cert){
|
||||
|
||||
$scope.loadingCert = true;
|
||||
$scope.viewCert = '';
|
||||
$scope.selectedCert = cert;
|
||||
|
||||
$api.request({
|
||||
module: 'Papers',
|
||||
action: 'loadCertProps',
|
||||
certName: cert
|
||||
},function(response){
|
||||
$scope.loadingCert = false;
|
||||
if (response === false) {
|
||||
$('#viewCert').modal('hide');
|
||||
return;
|
||||
}
|
||||
$scope.viewCert = response.data;
|
||||
});
|
||||
});
|
||||
|
||||
$scope.selectKey = (function(key, type) {
|
||||
$scope.certEncryptAlgo = "aes256";
|
||||
$scope.certEncryptPassword = '';
|
||||
$scope.selectedKey = key;
|
||||
$scope.selectedKeyType = type;
|
||||
});
|
||||
|
||||
$scope.encryptKey = (function(name, type, algo, pass) {
|
||||
|
||||
if (pass.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.encrypting = true;
|
||||
|
||||
$api.request({
|
||||
module: 'Papers',
|
||||
action: 'encryptKey',
|
||||
keyName: name,
|
||||
keyType: type,
|
||||
keyAlgo: algo,
|
||||
keyPass: pass
|
||||
},function(response){
|
||||
|
||||
$scope.encrypting = false;
|
||||
$scope.certEncryptPassword = '';
|
||||
|
||||
if (response.success === false) {
|
||||
alert("Failed to encrypt key. Check the logs for more info.");
|
||||
$scope.getLogs();
|
||||
return;
|
||||
}
|
||||
$scope.loadCertificates();
|
||||
$('#encryptModal').modal('hide');
|
||||
});
|
||||
});
|
||||
|
||||
$scope.decryptKey = (function(name, type, pass) {
|
||||
|
||||
if (pass.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.decrypting = true;
|
||||
|
||||
$api.request({
|
||||
module: 'Papers',
|
||||
action: 'decryptKey',
|
||||
keyName: name,
|
||||
keyType: type,
|
||||
keyPass: pass
|
||||
},function(response){
|
||||
|
||||
$scope.decrypting = false;
|
||||
$scope.certDecryptPassword = '';
|
||||
|
||||
if (response.success === false) {
|
||||
alert("Failed to decrypt key. Ensure you have entered the password correctly.");
|
||||
$scope.getLogs();
|
||||
return;
|
||||
}
|
||||
$scope.loadCertificates();
|
||||
$('#decryptModal').modal('hide');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
$scope.clearForm = (function() {
|
||||
$scope.certKeyType = "tls_ssl";
|
||||
@ -263,7 +359,6 @@ registerController('PapersController', ['$api', '$scope', '$sce', '$http', funct
|
||||
}
|
||||
} else {
|
||||
// Alert error
|
||||
alert(response.message);
|
||||
}
|
||||
$scope.loadCertificates();
|
||||
});
|
||||
@ -350,6 +445,7 @@ registerController('PapersController', ['$api', '$scope', '$sce', '$http', funct
|
||||
parameters: log,
|
||||
type: type
|
||||
},function(response){
|
||||
console.log(response);
|
||||
if (response.success === true) {
|
||||
$scope.currentLogData = $sce.trustAsHtml(response.data);
|
||||
}
|
||||
|
||||
@ -113,6 +113,14 @@ $(document).on('mouseenter', '.papers_hoverDanger', function() {
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm papers_hoverDanger" ng-show="data.Authorized==true" ng-click="revokeSSHKey(data.Name);"><img src="/modules/Papers/includes/icons/glyphicons-205-unlock.png"/></button>
|
||||
<button type="button" class="btn btn-sm papers_hoverSuccess" ng-disabled="data.Encrypted == 'Yes' && data.KeyType == 'TLS/SSL'" ng-show="data.Authorized==false" ng-click="securePineapple(data.Name, data.KeyType);"><img src="/modules/Papers/includes/icons/glyphicons-204-lock.png"/></button>
|
||||
|
||||
<!-- Encrypt button -->
|
||||
<button type="button" class="btn btn-sm papers_hoverSuccess" data-toggle="modal" data-target="#encryptModal" ng-show="data.Encrypted == 'No'" ng-click="selectKey(data.Name, data.KeyType);"><img src="/modules/Papers/includes/icons/glyphicons-45-keys.png"/></button>
|
||||
|
||||
<!-- Decrypt button -->
|
||||
<button type="button" class="btn btn-sm btn-success papers_hoverDanger" data-toggle="modal" data-target="#decryptModal" ng-show="data.Encrypted == 'Yes'" ng-click="selectKey(data.Name, data.KeyType);"><img src="/modules/Papers/includes/icons/glyphicons-45-keys.png"/></button>
|
||||
|
||||
<button type="button" class="btn btn-sm papers_hoverInfo" ng-disabled="data.KeyType == 'SSH'" data-toggle="modal" data-target="#viewCert" ng-click="loadCertProps(data.Name);"><img src="/modules/Papers/includes/icons/glyphicons-28-search.png"/></button>
|
||||
<button type="button" class="btn btn-sm papers_hoverInfo" ng-click="downloadKeys(data.Name, data.KeyType);"><img src="/modules/Papers/includes/icons/glyphicons-201-download.png"/></button>
|
||||
<button type="button" class="btn btn-sm papers_hoverDanger" ng-click="deleteKeys(data.Name, data.KeyType);"><img src="/modules/Papers/includes/icons/glyphicons-17-bin.png"/></button>
|
||||
</td>
|
||||
@ -200,8 +208,8 @@ $(document).on('mouseenter', '.papers_hoverDanger', function() {
|
||||
<hr />
|
||||
<div class="form-group" ng-show="certKeyType=='tls_ssl'" ng-hide="certKeyType=='ssh'">
|
||||
<label class="col-md-2 control-label">Signature Algorithm</label>
|
||||
<div class="col-md-8">
|
||||
<select name="sigalgo" ng-model="certSigAlgo" required>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" style="width: auto" name="sigalgo" ng-model="certSigAlgo" required>
|
||||
<option value="sha1">SHA-1</option>
|
||||
<option value="sha256">SHA-256</option>
|
||||
<option value="sha512">SHA-512</option>
|
||||
@ -269,15 +277,12 @@ $(document).on('mouseenter', '.papers_hoverDanger', function() {
|
||||
<div class="form-group" ng-show="certKeyType=='tls_ssl'" ng-hide="certKeyType=='ssh'">
|
||||
<label class="col-md-2 control-label">Algorithm</label>
|
||||
<div class="col-md-8">
|
||||
<select name="algo" ng-model="certEncryptAlgo">
|
||||
<select class="form-control" style="width:auto" name="algo" ng-model="certEncryptAlgo">
|
||||
<option value="aes128">AES 128</option>
|
||||
<option value="aes192">AES 192</option>
|
||||
<option value="aes256">AES 256</option>
|
||||
<option value="des">DES</option>
|
||||
<option value="des3">3DES</option>
|
||||
<option value="camellia128">Camellia 128</option>
|
||||
<option value="camellia192">Camellia 192</option>
|
||||
<option value="camellia256">Camellia 256</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -298,15 +303,12 @@ $(document).on('mouseenter', '.papers_hoverDanger', function() {
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Container Algorithm</label>
|
||||
<div class="col-md-8">
|
||||
<select name="containerAlgo" ng-model="certEncryptPKCS12Algo">
|
||||
<select class="form-control" style="width:auto" name="containerAlgo" ng-model="certEncryptPKCS12Algo">
|
||||
<option value="aes128">AES 128</option>
|
||||
<option value="aes192">AES 192</option>
|
||||
<option value="aes256">AES 256</option>
|
||||
<option value="des">DES</option>
|
||||
<option value="des3">3DES</option>
|
||||
<option value="camellia128">Camellia 128</option>
|
||||
<option value="camellia192">Camellia 192</option>
|
||||
<option value="camellia256">Camellia 256</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -323,6 +325,62 @@ $(document).on('mouseenter', '.papers_hoverDanger', function() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="viewCert" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h3>Certificate Properties: {{ selectedCert }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<img ng-show="loadingCert" ng-hide="!loadingCert" src='/img/throbber.gif'/>
|
||||
|
||||
<table class="table table-striped" ng-show="!loadingCert" ng-hide="loadingCert">
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>Serial</td>
|
||||
<td>{{ viewCert.serial }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Subject</td>
|
||||
<td>{{ viewCert.subject }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Issuer</td>
|
||||
<td>{{ viewCert.issuer }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Subject Alt Names</td>
|
||||
<td>{{ viewCert.dns }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Start Date</td>
|
||||
<td>{{ viewCert.start }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>End Date</td>
|
||||
<td>{{ viewCert.end }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Fingerprint</td>
|
||||
<td>{{ viewCert.fingerprint }}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="viewLogInfo" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@ -336,7 +394,75 @@ $(document).on('mouseenter', '.papers_hoverDanger', function() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="encryptModal" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h3>Key Encryption : {{ selectedKey }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Algorithm</label>
|
||||
<div class="col-md-9">
|
||||
<select class="form-control" style="width:auto" name="algo" ng-model="certEncryptAlgo">
|
||||
<option value="aes128">AES 128</option>
|
||||
<option value="aes192">AES 192</option>
|
||||
<option value="aes256">AES 256</option>
|
||||
<option value="des">DES</option>
|
||||
<option value="des3">3DES</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Key Password</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" class="form-control" ng-model="certEncryptPassword">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-12 text-center">
|
||||
<img ng-show="encrypting" ng-hide="!encrypting" src='/img/throbber.gif'/>
|
||||
<button type="button" class="btn papers_hoverInfo" ng-show="!encrypting" ng-hide="encrypting" ng-disabled="certEncryptPassword == ''" ng-click="encryptKey(selectedKey, selectedKeyType, certEncryptAlgo, certEncryptPassword);" style="width: 250px">Encrypt</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="decryptModal" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h3>Key Decryption : {{ selectedKey }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Key Password</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" class="form-control" ng-model="certDecryptPassword">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-12 text-center">
|
||||
<img ng-show="decrypting" ng-hide="!decrypting" src='/img/throbber.gif'/>
|
||||
<button type="button" class="btn papers_hoverInfo" ng-show="!decrypting" ng-hide="decrypting" ng-disabled="certDecryptPassword == ''" ng-click="decryptKey(selectedKey, selectedKeyType, certDecryptPassword);" style="width: 250px">Decrypt</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading pointer" data-toggle="collapse" data-target="#papers_errorLogs">
|
||||
|
||||
@ -6,5 +6,5 @@
|
||||
"tetra"
|
||||
],
|
||||
"title": "Papers",
|
||||
"version": "1.5"
|
||||
"version": "1.6"
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
# PortalAuth
|
||||
Captive portal cloner and payload distributor for the WiFi Pineapple NANO and TETRA
|
||||
# PortalAuth
|
||||
Captive portal cloner and payload distributor for the WiFi Pineapple NANO and TETRA
|
||||
|
||||
3
PortalAuth/includes/changelog/Version 1.8
Normal file
3
PortalAuth/includes/changelog/Version 1.8
Normal file
@ -0,0 +1,3 @@
|
||||
July 26, 2018
|
||||
<br /><br />
|
||||
- Fixed tinycss lib reference<br />
|
||||
5
PortalAuth/includes/changelog/Version 1.9
Normal file
5
PortalAuth/includes/changelog/Version 1.9
Normal file
@ -0,0 +1,5 @@
|
||||
Dec 25, 2018
|
||||
<br /><br />
|
||||
- Moved captive portal test page to AWS S3<br />
|
||||
- Added exception handling to CSS parser<br />
|
||||
- Fixed typo in PortalCloner.py
|
||||
@ -153,23 +153,26 @@ class PortalCloner:
|
||||
# Search for CSS files linked with the @import statement and remove
|
||||
for style in self.soup.find_all("style"):
|
||||
parser = tinycss.make_parser('page3')
|
||||
stylesheet = parser.parse_stylesheet(style.string)
|
||||
for rule in stylesheet.rules:
|
||||
if rule.at_keyword == "@import":
|
||||
|
||||
# Get the name of the resource
|
||||
fname = str(rule.uri).split("/")[-1]
|
||||
try:
|
||||
stylesheet = parser.parse_stylesheet(style.string)
|
||||
for rule in stylesheet.rules:
|
||||
if rule.at_keyword == "@import":
|
||||
|
||||
# Download the resource
|
||||
resourceURLs.append([rul.uri, fname])
|
||||
|
||||
# Parse the CSS to get image links
|
||||
_key = "resources/" + fname
|
||||
self.css_urls[_key] = self.parseCSS(urlparse.urljoin(self.url, rule.uri))
|
||||
|
||||
# Replace the old link of the CSS with the new one
|
||||
modStyle = style.string
|
||||
style.string.replace_with(modStyle.replace(rule.uri, "resources/" + fname))
|
||||
# Get the name of the resource
|
||||
fname = str(rule.uri).split("/")[-1]
|
||||
|
||||
# Download the resource
|
||||
resourceURLs.append([rule.uri, fname])
|
||||
|
||||
# Parse the CSS to get image links
|
||||
_key = "resources/" + fname
|
||||
self.css_urls[_key] = self.parseCSS(urlparse.urljoin(self.url, rule.uri))
|
||||
|
||||
# Replace the old link of the CSS with the new one
|
||||
modStyle = style.string
|
||||
style.string.replace_with(modStyle.replace(rule.uri, "resources/" + fname))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# Find and download all images and CSS files linked with <link>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,310 +1,310 @@
|
||||
# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software and its
|
||||
# documentation for any purpose and without fee is hereby granted,
|
||||
# provided that the above copyright notice appear in all copies and that
|
||||
# both that copyright notice and this permission notice appear in
|
||||
# supporting documentation, and that the name of Vinay Sajip
|
||||
# not be used in advertising or publicity pertaining to distribution
|
||||
# of the software without specific, written prior permission.
|
||||
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||||
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||||
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""
|
||||
Configuration functions for the logging package for Python. The core package
|
||||
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
|
||||
by Apache's log4j system.
|
||||
|
||||
Should work under Python versions >= 1.5.2, except that source line
|
||||
information is not available unless 'sys._getframe()' is.
|
||||
|
||||
Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
|
||||
|
||||
To use, simply 'import logging' and log away!
|
||||
"""
|
||||
|
||||
import sys, logging, logging.handlers, string, socket, struct, os
|
||||
|
||||
try:
|
||||
import thread
|
||||
import threading
|
||||
except ImportError:
|
||||
thread = None
|
||||
|
||||
from SocketServer import ThreadingTCPServer, StreamRequestHandler
|
||||
|
||||
|
||||
DEFAULT_LOGGING_CONFIG_PORT = 9030
|
||||
|
||||
if sys.platform == "win32":
|
||||
RESET_ERROR = 10054 #WSAECONNRESET
|
||||
else:
|
||||
RESET_ERROR = 104 #ECONNRESET
|
||||
|
||||
#
|
||||
# The following code implements a socket listener for on-the-fly
|
||||
# reconfiguration of logging.
|
||||
#
|
||||
# _listener holds the server object doing the listening
|
||||
_listener = None
|
||||
|
||||
def fileConfig(fname, defaults=None):
|
||||
"""
|
||||
Read the logging configuration from a ConfigParser-format file.
|
||||
|
||||
This can be called several times from an application, allowing an end user
|
||||
the ability to select from various pre-canned configurations (if the
|
||||
developer provides a mechanism to present the choices and load the chosen
|
||||
configuration).
|
||||
In versions of ConfigParser which have the readfp method [typically
|
||||
shipped in 2.x versions of Python], you can pass in a file-like object
|
||||
rather than a filename, in which case the file-like object will be read
|
||||
using readfp.
|
||||
"""
|
||||
import ConfigParser
|
||||
|
||||
cp = ConfigParser.ConfigParser(defaults)
|
||||
if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
|
||||
cp.readfp(fname)
|
||||
else:
|
||||
cp.read(fname)
|
||||
#first, do the formatters...
|
||||
flist = cp.get("formatters", "keys")
|
||||
if len(flist):
|
||||
flist = string.split(flist, ",")
|
||||
formatters = {}
|
||||
for form in flist:
|
||||
sectname = "formatter_%s" % form
|
||||
opts = cp.options(sectname)
|
||||
if "format" in opts:
|
||||
fs = cp.get(sectname, "format", 1)
|
||||
else:
|
||||
fs = None
|
||||
if "datefmt" in opts:
|
||||
dfs = cp.get(sectname, "datefmt", 1)
|
||||
else:
|
||||
dfs = None
|
||||
f = logging.Formatter(fs, dfs)
|
||||
formatters[form] = f
|
||||
#next, do the handlers...
|
||||
#critical section...
|
||||
logging._acquireLock()
|
||||
try:
|
||||
try:
|
||||
#first, lose the existing handlers...
|
||||
logging._handlers.clear()
|
||||
#now set up the new ones...
|
||||
hlist = cp.get("handlers", "keys")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
handlers = {}
|
||||
fixups = [] #for inter-handler references
|
||||
for hand in hlist:
|
||||
try:
|
||||
sectname = "handler_%s" % hand
|
||||
klass = cp.get(sectname, "class")
|
||||
opts = cp.options(sectname)
|
||||
if "formatter" in opts:
|
||||
fmt = cp.get(sectname, "formatter")
|
||||
else:
|
||||
fmt = ""
|
||||
klass = eval(klass, vars(logging))
|
||||
args = cp.get(sectname, "args")
|
||||
args = eval(args, vars(logging))
|
||||
h = apply(klass, args)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
h.setLevel(logging._levelNames[level])
|
||||
if len(fmt):
|
||||
h.setFormatter(formatters[fmt])
|
||||
#temporary hack for FileHandler and MemoryHandler.
|
||||
if klass == logging.handlers.MemoryHandler:
|
||||
if "target" in opts:
|
||||
target = cp.get(sectname,"target")
|
||||
else:
|
||||
target = ""
|
||||
if len(target): #the target handler may not be loaded yet, so keep for later...
|
||||
fixups.append((h, target))
|
||||
handlers[hand] = h
|
||||
except: #if an error occurs when instantiating a handler, too bad
|
||||
pass #this could happen e.g. because of lack of privileges
|
||||
#now all handlers are loaded, fixup inter-handler references...
|
||||
for fixup in fixups:
|
||||
h = fixup[0]
|
||||
t = fixup[1]
|
||||
h.setTarget(handlers[t])
|
||||
#at last, the loggers...first the root...
|
||||
llist = cp.get("loggers", "keys")
|
||||
llist = string.split(llist, ",")
|
||||
llist.remove("root")
|
||||
sectname = "logger_root"
|
||||
root = logging.root
|
||||
log = root
|
||||
opts = cp.options(sectname)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
log.setLevel(logging._levelNames[level])
|
||||
for h in root.handlers[:]:
|
||||
root.removeHandler(h)
|
||||
hlist = cp.get(sectname, "handlers")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
for hand in hlist:
|
||||
log.addHandler(handlers[hand])
|
||||
#and now the others...
|
||||
#we don't want to lose the existing loggers,
|
||||
#since other threads may have pointers to them.
|
||||
#existing is set to contain all existing loggers,
|
||||
#and as we go through the new configuration we
|
||||
#remove any which are configured. At the end,
|
||||
#what's left in existing is the set of loggers
|
||||
#which were in the previous configuration but
|
||||
#which are not in the new configuration.
|
||||
existing = root.manager.loggerDict.keys()
|
||||
#now set up the new ones...
|
||||
for log in llist:
|
||||
sectname = "logger_%s" % log
|
||||
qn = cp.get(sectname, "qualname")
|
||||
opts = cp.options(sectname)
|
||||
if "propagate" in opts:
|
||||
propagate = cp.getint(sectname, "propagate")
|
||||
else:
|
||||
propagate = 1
|
||||
logger = logging.getLogger(qn)
|
||||
if qn in existing:
|
||||
existing.remove(qn)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
logger.setLevel(logging._levelNames[level])
|
||||
for h in logger.handlers[:]:
|
||||
logger.removeHandler(h)
|
||||
logger.propagate = propagate
|
||||
logger.disabled = 0
|
||||
hlist = cp.get(sectname, "handlers")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
for hand in hlist:
|
||||
logger.addHandler(handlers[hand])
|
||||
#Disable any old loggers. There's no point deleting
|
||||
#them as other threads may continue to hold references
|
||||
#and by disabling them, you stop them doing any logging.
|
||||
for log in existing:
|
||||
root.manager.loggerDict[log].disabled = 1
|
||||
except:
|
||||
import traceback
|
||||
ei = sys.exc_info()
|
||||
traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
|
||||
del ei
|
||||
finally:
|
||||
logging._releaseLock()
|
||||
|
||||
def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
|
||||
"""
|
||||
Start up a socket server on the specified port, and listen for new
|
||||
configurations.
|
||||
|
||||
These will be sent as a file suitable for processing by fileConfig().
|
||||
Returns a Thread object on which you can call start() to start the server,
|
||||
and which you can join() when appropriate. To stop the server, call
|
||||
stopListening().
|
||||
"""
|
||||
if not thread:
|
||||
raise NotImplementedError, "listen() needs threading to work"
|
||||
|
||||
class ConfigStreamHandler(StreamRequestHandler):
|
||||
"""
|
||||
Handler for a logging configuration request.
|
||||
|
||||
It expects a completely new logging configuration and uses fileConfig
|
||||
to install it.
|
||||
"""
|
||||
def handle(self):
|
||||
"""
|
||||
Handle a request.
|
||||
|
||||
Each request is expected to be a 4-byte length,
|
||||
followed by the config file. Uses fileConfig() to do the
|
||||
grunt work.
|
||||
"""
|
||||
import tempfile
|
||||
try:
|
||||
conn = self.connection
|
||||
chunk = conn.recv(4)
|
||||
if len(chunk) == 4:
|
||||
slen = struct.unpack(">L", chunk)[0]
|
||||
chunk = self.connection.recv(slen)
|
||||
while len(chunk) < slen:
|
||||
chunk = chunk + conn.recv(slen - len(chunk))
|
||||
#Apply new configuration. We'd like to be able to
|
||||
#create a StringIO and pass that in, but unfortunately
|
||||
#1.5.2 ConfigParser does not support reading file
|
||||
#objects, only actual files. So we create a temporary
|
||||
#file and remove it later.
|
||||
file = tempfile.mktemp(".ini")
|
||||
f = open(file, "w")
|
||||
f.write(chunk)
|
||||
f.close()
|
||||
fileConfig(file)
|
||||
os.remove(file)
|
||||
except socket.error, e:
|
||||
if type(e.args) != types.TupleType:
|
||||
raise
|
||||
else:
|
||||
errcode = e.args[0]
|
||||
if errcode != RESET_ERROR:
|
||||
raise
|
||||
|
||||
class ConfigSocketReceiver(ThreadingTCPServer):
|
||||
"""
|
||||
A simple TCP socket-based logging config receiver.
|
||||
"""
|
||||
|
||||
allow_reuse_address = 1
|
||||
|
||||
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
|
||||
handler=None):
|
||||
ThreadingTCPServer.__init__(self, (host, port), handler)
|
||||
logging._acquireLock()
|
||||
self.abort = 0
|
||||
logging._releaseLock()
|
||||
self.timeout = 1
|
||||
|
||||
def serve_until_stopped(self):
|
||||
import select
|
||||
abort = 0
|
||||
while not abort:
|
||||
rd, wr, ex = select.select([self.socket.fileno()],
|
||||
[], [],
|
||||
self.timeout)
|
||||
if rd:
|
||||
self.handle_request()
|
||||
logging._acquireLock()
|
||||
abort = self.abort
|
||||
logging._releaseLock()
|
||||
|
||||
def serve(rcvr, hdlr, port):
|
||||
server = rcvr(port=port, handler=hdlr)
|
||||
global _listener
|
||||
logging._acquireLock()
|
||||
_listener = server
|
||||
logging._releaseLock()
|
||||
server.serve_until_stopped()
|
||||
|
||||
return threading.Thread(target=serve,
|
||||
args=(ConfigSocketReceiver,
|
||||
ConfigStreamHandler, port))
|
||||
|
||||
def stopListening():
|
||||
"""
|
||||
Stop the listening server which was created with a call to listen().
|
||||
"""
|
||||
global _listener
|
||||
if _listener:
|
||||
logging._acquireLock()
|
||||
_listener.abort = 1
|
||||
_listener = None
|
||||
logging._releaseLock()
|
||||
# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software and its
|
||||
# documentation for any purpose and without fee is hereby granted,
|
||||
# provided that the above copyright notice appear in all copies and that
|
||||
# both that copyright notice and this permission notice appear in
|
||||
# supporting documentation, and that the name of Vinay Sajip
|
||||
# not be used in advertising or publicity pertaining to distribution
|
||||
# of the software without specific, written prior permission.
|
||||
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||||
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||||
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""
|
||||
Configuration functions for the logging package for Python. The core package
|
||||
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
|
||||
by Apache's log4j system.
|
||||
|
||||
Should work under Python versions >= 1.5.2, except that source line
|
||||
information is not available unless 'sys._getframe()' is.
|
||||
|
||||
Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
|
||||
|
||||
To use, simply 'import logging' and log away!
|
||||
"""
|
||||
|
||||
import sys, logging, logging.handlers, string, socket, struct, os
|
||||
|
||||
try:
|
||||
import thread
|
||||
import threading
|
||||
except ImportError:
|
||||
thread = None
|
||||
|
||||
from SocketServer import ThreadingTCPServer, StreamRequestHandler
|
||||
|
||||
|
||||
DEFAULT_LOGGING_CONFIG_PORT = 9030
|
||||
|
||||
if sys.platform == "win32":
|
||||
RESET_ERROR = 10054 #WSAECONNRESET
|
||||
else:
|
||||
RESET_ERROR = 104 #ECONNRESET
|
||||
|
||||
#
|
||||
# The following code implements a socket listener for on-the-fly
|
||||
# reconfiguration of logging.
|
||||
#
|
||||
# _listener holds the server object doing the listening
|
||||
_listener = None
|
||||
|
||||
def fileConfig(fname, defaults=None):
|
||||
"""
|
||||
Read the logging configuration from a ConfigParser-format file.
|
||||
|
||||
This can be called several times from an application, allowing an end user
|
||||
the ability to select from various pre-canned configurations (if the
|
||||
developer provides a mechanism to present the choices and load the chosen
|
||||
configuration).
|
||||
In versions of ConfigParser which have the readfp method [typically
|
||||
shipped in 2.x versions of Python], you can pass in a file-like object
|
||||
rather than a filename, in which case the file-like object will be read
|
||||
using readfp.
|
||||
"""
|
||||
import ConfigParser
|
||||
|
||||
cp = ConfigParser.ConfigParser(defaults)
|
||||
if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
|
||||
cp.readfp(fname)
|
||||
else:
|
||||
cp.read(fname)
|
||||
#first, do the formatters...
|
||||
flist = cp.get("formatters", "keys")
|
||||
if len(flist):
|
||||
flist = string.split(flist, ",")
|
||||
formatters = {}
|
||||
for form in flist:
|
||||
sectname = "formatter_%s" % form
|
||||
opts = cp.options(sectname)
|
||||
if "format" in opts:
|
||||
fs = cp.get(sectname, "format", 1)
|
||||
else:
|
||||
fs = None
|
||||
if "datefmt" in opts:
|
||||
dfs = cp.get(sectname, "datefmt", 1)
|
||||
else:
|
||||
dfs = None
|
||||
f = logging.Formatter(fs, dfs)
|
||||
formatters[form] = f
|
||||
#next, do the handlers...
|
||||
#critical section...
|
||||
logging._acquireLock()
|
||||
try:
|
||||
try:
|
||||
#first, lose the existing handlers...
|
||||
logging._handlers.clear()
|
||||
#now set up the new ones...
|
||||
hlist = cp.get("handlers", "keys")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
handlers = {}
|
||||
fixups = [] #for inter-handler references
|
||||
for hand in hlist:
|
||||
try:
|
||||
sectname = "handler_%s" % hand
|
||||
klass = cp.get(sectname, "class")
|
||||
opts = cp.options(sectname)
|
||||
if "formatter" in opts:
|
||||
fmt = cp.get(sectname, "formatter")
|
||||
else:
|
||||
fmt = ""
|
||||
klass = eval(klass, vars(logging))
|
||||
args = cp.get(sectname, "args")
|
||||
args = eval(args, vars(logging))
|
||||
h = apply(klass, args)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
h.setLevel(logging._levelNames[level])
|
||||
if len(fmt):
|
||||
h.setFormatter(formatters[fmt])
|
||||
#temporary hack for FileHandler and MemoryHandler.
|
||||
if klass == logging.handlers.MemoryHandler:
|
||||
if "target" in opts:
|
||||
target = cp.get(sectname,"target")
|
||||
else:
|
||||
target = ""
|
||||
if len(target): #the target handler may not be loaded yet, so keep for later...
|
||||
fixups.append((h, target))
|
||||
handlers[hand] = h
|
||||
except: #if an error occurs when instantiating a handler, too bad
|
||||
pass #this could happen e.g. because of lack of privileges
|
||||
#now all handlers are loaded, fixup inter-handler references...
|
||||
for fixup in fixups:
|
||||
h = fixup[0]
|
||||
t = fixup[1]
|
||||
h.setTarget(handlers[t])
|
||||
#at last, the loggers...first the root...
|
||||
llist = cp.get("loggers", "keys")
|
||||
llist = string.split(llist, ",")
|
||||
llist.remove("root")
|
||||
sectname = "logger_root"
|
||||
root = logging.root
|
||||
log = root
|
||||
opts = cp.options(sectname)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
log.setLevel(logging._levelNames[level])
|
||||
for h in root.handlers[:]:
|
||||
root.removeHandler(h)
|
||||
hlist = cp.get(sectname, "handlers")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
for hand in hlist:
|
||||
log.addHandler(handlers[hand])
|
||||
#and now the others...
|
||||
#we don't want to lose the existing loggers,
|
||||
#since other threads may have pointers to them.
|
||||
#existing is set to contain all existing loggers,
|
||||
#and as we go through the new configuration we
|
||||
#remove any which are configured. At the end,
|
||||
#what's left in existing is the set of loggers
|
||||
#which were in the previous configuration but
|
||||
#which are not in the new configuration.
|
||||
existing = root.manager.loggerDict.keys()
|
||||
#now set up the new ones...
|
||||
for log in llist:
|
||||
sectname = "logger_%s" % log
|
||||
qn = cp.get(sectname, "qualname")
|
||||
opts = cp.options(sectname)
|
||||
if "propagate" in opts:
|
||||
propagate = cp.getint(sectname, "propagate")
|
||||
else:
|
||||
propagate = 1
|
||||
logger = logging.getLogger(qn)
|
||||
if qn in existing:
|
||||
existing.remove(qn)
|
||||
if "level" in opts:
|
||||
level = cp.get(sectname, "level")
|
||||
logger.setLevel(logging._levelNames[level])
|
||||
for h in logger.handlers[:]:
|
||||
logger.removeHandler(h)
|
||||
logger.propagate = propagate
|
||||
logger.disabled = 0
|
||||
hlist = cp.get(sectname, "handlers")
|
||||
if len(hlist):
|
||||
hlist = string.split(hlist, ",")
|
||||
for hand in hlist:
|
||||
logger.addHandler(handlers[hand])
|
||||
#Disable any old loggers. There's no point deleting
|
||||
#them as other threads may continue to hold references
|
||||
#and by disabling them, you stop them doing any logging.
|
||||
for log in existing:
|
||||
root.manager.loggerDict[log].disabled = 1
|
||||
except:
|
||||
import traceback
|
||||
ei = sys.exc_info()
|
||||
traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
|
||||
del ei
|
||||
finally:
|
||||
logging._releaseLock()
|
||||
|
||||
def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
|
||||
"""
|
||||
Start up a socket server on the specified port, and listen for new
|
||||
configurations.
|
||||
|
||||
These will be sent as a file suitable for processing by fileConfig().
|
||||
Returns a Thread object on which you can call start() to start the server,
|
||||
and which you can join() when appropriate. To stop the server, call
|
||||
stopListening().
|
||||
"""
|
||||
if not thread:
|
||||
raise NotImplementedError, "listen() needs threading to work"
|
||||
|
||||
class ConfigStreamHandler(StreamRequestHandler):
|
||||
"""
|
||||
Handler for a logging configuration request.
|
||||
|
||||
It expects a completely new logging configuration and uses fileConfig
|
||||
to install it.
|
||||
"""
|
||||
def handle(self):
|
||||
"""
|
||||
Handle a request.
|
||||
|
||||
Each request is expected to be a 4-byte length,
|
||||
followed by the config file. Uses fileConfig() to do the
|
||||
grunt work.
|
||||
"""
|
||||
import tempfile
|
||||
try:
|
||||
conn = self.connection
|
||||
chunk = conn.recv(4)
|
||||
if len(chunk) == 4:
|
||||
slen = struct.unpack(">L", chunk)[0]
|
||||
chunk = self.connection.recv(slen)
|
||||
while len(chunk) < slen:
|
||||
chunk = chunk + conn.recv(slen - len(chunk))
|
||||
#Apply new configuration. We'd like to be able to
|
||||
#create a StringIO and pass that in, but unfortunately
|
||||
#1.5.2 ConfigParser does not support reading file
|
||||
#objects, only actual files. So we create a temporary
|
||||
#file and remove it later.
|
||||
file = tempfile.mktemp(".ini")
|
||||
f = open(file, "w")
|
||||
f.write(chunk)
|
||||
f.close()
|
||||
fileConfig(file)
|
||||
os.remove(file)
|
||||
except socket.error, e:
|
||||
if type(e.args) != types.TupleType:
|
||||
raise
|
||||
else:
|
||||
errcode = e.args[0]
|
||||
if errcode != RESET_ERROR:
|
||||
raise
|
||||
|
||||
class ConfigSocketReceiver(ThreadingTCPServer):
|
||||
"""
|
||||
A simple TCP socket-based logging config receiver.
|
||||
"""
|
||||
|
||||
allow_reuse_address = 1
|
||||
|
||||
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
|
||||
handler=None):
|
||||
ThreadingTCPServer.__init__(self, (host, port), handler)
|
||||
logging._acquireLock()
|
||||
self.abort = 0
|
||||
logging._releaseLock()
|
||||
self.timeout = 1
|
||||
|
||||
def serve_until_stopped(self):
|
||||
import select
|
||||
abort = 0
|
||||
while not abort:
|
||||
rd, wr, ex = select.select([self.socket.fileno()],
|
||||
[], [],
|
||||
self.timeout)
|
||||
if rd:
|
||||
self.handle_request()
|
||||
logging._acquireLock()
|
||||
abort = self.abort
|
||||
logging._releaseLock()
|
||||
|
||||
def serve(rcvr, hdlr, port):
|
||||
server = rcvr(port=port, handler=hdlr)
|
||||
global _listener
|
||||
logging._acquireLock()
|
||||
_listener = server
|
||||
logging._releaseLock()
|
||||
server.serve_until_stopped()
|
||||
|
||||
return threading.Thread(target=serve,
|
||||
args=(ConfigSocketReceiver,
|
||||
ConfigStreamHandler, port))
|
||||
|
||||
def stopListening():
|
||||
"""
|
||||
Stop the listening server which was created with a call to listen().
|
||||
"""
|
||||
global _listener
|
||||
if _listener:
|
||||
logging._acquireLock()
|
||||
_listener.abort = 1
|
||||
_listener = None
|
||||
logging._releaseLock()
|
||||
|
||||
200
PortalAuth/includes/scripts/libs/tinycss/fonts3.py
Normal file
200
PortalAuth/includes/scripts/libs/tinycss/fonts3.py
Normal file
@ -0,0 +1,200 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
tinycss.colors3
|
||||
---------------
|
||||
|
||||
Parser for CSS 3 Fonts syntax:
|
||||
https://www.w3.org/TR/css-fonts-3/
|
||||
|
||||
Adds support for font-face and font-feature-values rules.
|
||||
|
||||
:copyright: (c) 2016 by Kozea.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import division, unicode_literals
|
||||
|
||||
from .css21 import CSS21Parser, ParseError
|
||||
|
||||
|
||||
class FontFaceRule(object):
|
||||
"""A parsed at-rule for font faces.
|
||||
|
||||
.. attribute:: at_keyword
|
||||
|
||||
Always ``'@font-face'``.
|
||||
|
||||
.. attribute:: declarations
|
||||
|
||||
A list of :class:`~.css21.Declaration` objects.
|
||||
|
||||
.. attribute:: line
|
||||
|
||||
Source line where this was read.
|
||||
|
||||
.. attribute:: column
|
||||
|
||||
Source column where this was read.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, at_keyword, declarations, line, column):
|
||||
assert at_keyword == '@font-face'
|
||||
self.at_keyword = at_keyword
|
||||
self.declarations = declarations
|
||||
self.line = line
|
||||
self.column = column
|
||||
|
||||
|
||||
class FontFeatureValuesRule(object):
|
||||
"""A parsed at-rule for font feature values.
|
||||
|
||||
.. attribute:: at_keyword
|
||||
|
||||
Always ``'@font-feature-values'``.
|
||||
|
||||
.. attribute:: line
|
||||
|
||||
Source line where this was read.
|
||||
|
||||
.. attribute:: column
|
||||
|
||||
Source column where this was read.
|
||||
|
||||
.. attribute:: at_rules
|
||||
|
||||
The list of parsed at-rules inside the @font-feature-values block, in
|
||||
source order.
|
||||
|
||||
.. attribute:: family_names
|
||||
|
||||
A list of strings representing font families.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, at_keyword, at_rules, family_names, line, column):
|
||||
assert at_keyword == '@font-feature-values'
|
||||
self.at_keyword = at_keyword
|
||||
self.family_names = family_names
|
||||
self.at_rules = at_rules
|
||||
self.line = line
|
||||
self.column = column
|
||||
|
||||
|
||||
class FontFeatureRule(object):
|
||||
"""A parsed at-rule for font features.
|
||||
|
||||
.. attribute:: at_keyword
|
||||
|
||||
One of the 16 following strings:
|
||||
|
||||
* ``@stylistic``
|
||||
* ``@styleset``
|
||||
* ``@character-variant``
|
||||
* ``@swash``
|
||||
* ``@ornaments``
|
||||
* ``@annotation``
|
||||
|
||||
.. attribute:: declarations
|
||||
|
||||
A list of :class:`~.css21.Declaration` objects.
|
||||
|
||||
.. attribute:: line
|
||||
|
||||
Source line where this was read.
|
||||
|
||||
.. attribute:: column
|
||||
|
||||
Source column where this was read.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, at_keyword, declarations, line, column):
|
||||
self.at_keyword = at_keyword
|
||||
self.declarations = declarations
|
||||
self.line = line
|
||||
self.column = column
|
||||
|
||||
|
||||
class CSSFonts3Parser(CSS21Parser):
|
||||
"""Extend :class:`~.css21.CSS21Parser` for `CSS 3 Fonts`_ syntax.
|
||||
|
||||
.. _CSS 3 Fonts: https://www.w3.org/TR/css-fonts-3/
|
||||
|
||||
"""
|
||||
|
||||
FONT_FEATURE_VALUES_AT_KEYWORDS = [
|
||||
'@stylistic',
|
||||
'@styleset',
|
||||
'@character-variant',
|
||||
'@swash',
|
||||
'@ornaments',
|
||||
'@annotation',
|
||||
]
|
||||
|
||||
def parse_at_rule(self, rule, previous_rules, errors, context):
|
||||
if rule.at_keyword == '@font-face':
|
||||
if rule.head:
|
||||
raise ParseError(
|
||||
rule.head[0],
|
||||
'unexpected {0} token in {1} rule header'.format(
|
||||
rule.head[0].type, rule.at_keyword))
|
||||
declarations, body_errors = self.parse_declaration_list(rule.body)
|
||||
errors.extend(body_errors)
|
||||
return FontFaceRule(
|
||||
rule.at_keyword, declarations, rule.line, rule.column)
|
||||
elif rule.at_keyword == '@font-feature-values':
|
||||
family_names = tuple(
|
||||
self.parse_font_feature_values_family_names(rule.head))
|
||||
at_rules, body_errors = (
|
||||
self.parse_rules(rule.body or [], '@font-feature-values'))
|
||||
errors.extend(body_errors)
|
||||
return FontFeatureValuesRule(
|
||||
rule.at_keyword, at_rules, family_names,
|
||||
rule.line, rule.column)
|
||||
elif rule.at_keyword in self.FONT_FEATURE_VALUES_AT_KEYWORDS:
|
||||
if context != '@font-feature-values':
|
||||
raise ParseError(
|
||||
rule, '{0} rule not allowed in {1}'.format(
|
||||
rule.at_keyword, context))
|
||||
declarations, body_errors = self.parse_declaration_list(rule.body)
|
||||
errors.extend(body_errors)
|
||||
return FontFeatureRule(
|
||||
rule.at_keyword, declarations, rule.line, rule.column)
|
||||
return super(CSSFonts3Parser, self).parse_at_rule(
|
||||
rule, previous_rules, errors, context)
|
||||
|
||||
def parse_font_feature_values_family_names(self, tokens):
|
||||
"""Parse an @font-feature-values selector.
|
||||
|
||||
:param tokens:
|
||||
An iterable of token, typically from the ``head`` attribute of
|
||||
an unparsed :class:`AtRule`.
|
||||
:returns:
|
||||
A generator of strings representing font families.
|
||||
:raises:
|
||||
:class:`~.parsing.ParseError` on invalid selectors
|
||||
|
||||
"""
|
||||
family = ''
|
||||
current_string = False
|
||||
for token in tokens:
|
||||
if token.type == 'DELIM' and token.value == ',' and family:
|
||||
yield family
|
||||
family = ''
|
||||
current_string = False
|
||||
elif token.type == 'STRING' and not family and (
|
||||
current_string is False):
|
||||
family = token.value
|
||||
current_string = True
|
||||
elif token.type == 'IDENT' and not current_string:
|
||||
if family:
|
||||
family += ' '
|
||||
family += token.value
|
||||
elif token.type != 'S':
|
||||
family = ''
|
||||
break
|
||||
if family:
|
||||
yield family
|
||||
else:
|
||||
raise ParseError(token, 'invalid @font-feature-values selector')
|
||||
7583
PortalAuth/includes/scripts/libs/tinycss/speedups.c
Normal file
7583
PortalAuth/includes/scripts/libs/tinycss/speedups.c
Normal file
File diff suppressed because it is too large
Load Diff
144
PortalAuth/includes/scripts/libs/tinycss/tests/test_fonts3.py
Normal file
144
PortalAuth/includes/scripts/libs/tinycss/tests/test_fonts3.py
Normal file
@ -0,0 +1,144 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Tests for the Fonts 3 parser
|
||||
----------------------------
|
||||
|
||||
:copyright: (c) 2016 by Kozea.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import pytest
|
||||
from tinycss.fonts3 import CSSFonts3Parser
|
||||
|
||||
from . import assert_errors
|
||||
from .test_tokenizer import jsonify
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('css', 'expected_family_names', 'expected_errors'), [
|
||||
('@font-feature-values foo {}', ('foo',), []),
|
||||
('@font-feature-values Foo Test {}', ('Foo Test',), []),
|
||||
('@font-feature-values \'Foo Test\' {}', ('Foo Test',), []),
|
||||
('@font-feature-values Foo Test, Foo Lol, "Foo tooo"', (
|
||||
'Foo Test', 'Foo Lol', 'Foo tooo'), []),
|
||||
('@font-feature-values Foo , Foo lol {}', ('Foo', 'Foo lol'), []),
|
||||
('@font-feature-values Foo , "Foobar" , Lol {}', (
|
||||
'Foo', 'Foobar', 'Lol'), []),
|
||||
('@font-feature-values Foo, {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
('@font-feature-values ,Foo {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
('@font-feature-values Test,"Foo", {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
('@font-feature-values Test "Foo" {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
('@font-feature-values Test Foo, Test "bar", "foo" {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
('@font-feature-values Test/Foo {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
('@font-feature-values /Foo {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
('@font-feature-values #Foo {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
# TODO: this currently works but should not work
|
||||
# ('@font-feature-values test@foo {}', None, [
|
||||
# 'invalid @font-feature-values selector']),
|
||||
('@font-feature-values Hawaii 5-0 {}', None, [
|
||||
'invalid @font-feature-values selector']),
|
||||
])
|
||||
def test_font_feature_values_selectors(css, expected_family_names,
|
||||
expected_errors):
|
||||
stylesheet = CSSFonts3Parser().parse_stylesheet(css)
|
||||
assert_errors(stylesheet.errors, expected_errors)
|
||||
|
||||
if stylesheet.rules:
|
||||
assert len(stylesheet.rules) == 1
|
||||
rule = stylesheet.rules[0]
|
||||
assert rule.at_keyword == '@font-feature-values'
|
||||
assert rule.family_names == expected_family_names
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('css', 'expected_declarations', 'expected_errors'), [
|
||||
('@font-face {}', [], []),
|
||||
('@font-face test { src: "lol"; font-family: "bar" }', None, [
|
||||
'unexpected IDENT token in @font-face rule header']),
|
||||
('@font-face { src: "lol"; font-family: "bar" }', [
|
||||
('src', [('STRING', 'lol')]),
|
||||
('font-family', [('STRING', 'bar')])], []),
|
||||
('@font-face { src: "lol"; font-family: "bar"; src: "baz" }', [
|
||||
('src', [('STRING', 'lol')]),
|
||||
('font-family', [('STRING', 'bar')]),
|
||||
('src', [('STRING', 'baz')])], []),
|
||||
])
|
||||
def test_font_face_content(css, expected_declarations, expected_errors):
|
||||
stylesheet = CSSFonts3Parser().parse_stylesheet(css)
|
||||
assert_errors(stylesheet.errors, expected_errors)
|
||||
|
||||
def declarations(rule):
|
||||
return [(decl.name, list(jsonify(decl.value)))
|
||||
for decl in rule.declarations]
|
||||
|
||||
if expected_declarations is None:
|
||||
assert stylesheet.rules == []
|
||||
assert expected_errors
|
||||
else:
|
||||
assert len(stylesheet.rules) == 1
|
||||
rule = stylesheet.rules[0]
|
||||
assert rule.at_keyword == '@font-face'
|
||||
assert declarations(rule) == expected_declarations
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('css', 'expected_rules', 'expected_errors'), [
|
||||
('''@annotation{}''', None, [
|
||||
'@annotation rule not allowed in stylesheet']),
|
||||
('''@font-feature-values foo {}''', None, []),
|
||||
('''@font-feature-values foo {
|
||||
@swash { ornate: 1; }
|
||||
@styleset { double-W: 14; sharp-terminals: 16 1; }
|
||||
}''', [
|
||||
('@swash', [('ornate', [('INTEGER', 1)])]),
|
||||
('@styleset', [
|
||||
('double-w', [('INTEGER', 14)]),
|
||||
('sharp-terminals', [
|
||||
('INTEGER', 16), ('S', ' '), ('INTEGER', 1)])])], []),
|
||||
('''@font-feature-values foo {
|
||||
@swash { ornate: 14; }
|
||||
@unknown { test: 1; }
|
||||
}''', [('@swash', [('ornate', [('INTEGER', 14)])])], [
|
||||
'unknown at-rule in @font-feature-values context: @unknown']),
|
||||
('''@font-feature-values foo {
|
||||
@annotation{boxed:1}
|
||||
bad: 2;
|
||||
@brokenstylesetbecauseofbadabove { sharp: 1}
|
||||
@styleset { sharp-terminals: 16 1; @bad {}}
|
||||
@styleset { @bad {} top-ignored: 3; top: 9000}
|
||||
really-bad
|
||||
}''', [
|
||||
('@annotation', [('boxed', [('INTEGER', 1)])]),
|
||||
('@styleset', [
|
||||
('sharp-terminals', [
|
||||
('INTEGER', 16), ('S', ' '), ('INTEGER', 1)])]),
|
||||
('@styleset', [('top', [('INTEGER', 9000)])])], [
|
||||
'unexpected ; token in selector',
|
||||
'expected a property name, got ATKEYWORD',
|
||||
'expected a property name, got ATKEYWORD',
|
||||
'no declaration block found for ruleset']),
|
||||
])
|
||||
def test_font_feature_values_content(css, expected_rules, expected_errors):
|
||||
stylesheet = CSSFonts3Parser().parse_stylesheet(css)
|
||||
assert_errors(stylesheet.errors, expected_errors)
|
||||
|
||||
if expected_rules is not None:
|
||||
assert len(stylesheet.rules) == 1
|
||||
rule = stylesheet.rules[0]
|
||||
assert rule.at_keyword == '@font-feature-values'
|
||||
|
||||
rules = [
|
||||
(at_rule.at_keyword, [
|
||||
(decl.name, list(jsonify(decl.value)))
|
||||
for decl in at_rule.declarations])
|
||||
for at_rule in rule.at_rules] if rule.at_rules else None
|
||||
assert rules == expected_rules
|
||||
302
PortalAuth/includes/scripts/libs/tinycss/tests/test_tokenizer.py
Normal file
302
PortalAuth/includes/scripts/libs/tinycss/tests/test_tokenizer.py
Normal file
@ -0,0 +1,302 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Tests for the tokenizer
|
||||
-----------------------
|
||||
|
||||
:copyright: (c) 2012 by Simon Sapin.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from tinycss.tokenizer import (
|
||||
cython_tokenize_flat, python_tokenize_flat, regroup)
|
||||
|
||||
|
||||
def test_speedups():
|
||||
is_pypy = hasattr(sys, 'pypy_translation_info')
|
||||
env_skip_tests = os.environ.get('TINYCSS_SKIP_SPEEDUPS_TESTS')
|
||||
# pragma: no cover
|
||||
if is_pypy or env_skip_tests:
|
||||
return
|
||||
assert cython_tokenize_flat is not None, (
|
||||
'Cython speedups are not installed, related tests will '
|
||||
'be skipped. Set the TINYCSS_SKIP_SPEEDUPS_TESTS environment '
|
||||
'variable if this is expected.')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('tokenize', 'css_source', 'expected_tokens'), [
|
||||
(tokenize,) + test_data
|
||||
for tokenize in (python_tokenize_flat, cython_tokenize_flat)
|
||||
for test_data in [
|
||||
('', []),
|
||||
('red -->', [('IDENT', 'red'), ('S', ' '), ('CDC', '-->')]),
|
||||
# Longest match rule: no CDC
|
||||
('red-->', [('IDENT', 'red--'), ('DELIM', '>')]),
|
||||
(r'p[example="foo(int x) { this.x = x;}"]', [
|
||||
('IDENT', 'p'),
|
||||
('[', '['),
|
||||
('IDENT', 'example'),
|
||||
('DELIM', '='),
|
||||
('STRING', 'foo(int x) { this.x = x;}'),
|
||||
(']', ']')]),
|
||||
|
||||
# Numbers are parsed
|
||||
('42 .5 -4pX 1.25em 30%', [
|
||||
('INTEGER', 42), ('S', ' '),
|
||||
('NUMBER', .5), ('S', ' '),
|
||||
# units are normalized to lower-case:
|
||||
('DIMENSION', -4, 'px'), ('S', ' '),
|
||||
('DIMENSION', 1.25, 'em'), ('S', ' '),
|
||||
('PERCENTAGE', 30, '%')]),
|
||||
|
||||
# URLs are extracted
|
||||
('url(foo.png)', [('URI', 'foo.png')]),
|
||||
('url("foo.png")', [('URI', 'foo.png')]),
|
||||
|
||||
# Escaping
|
||||
|
||||
(r'/* Comment with a \ backslash */', [
|
||||
('COMMENT', '/* Comment with a \ backslash */')]), # Unchanged
|
||||
|
||||
# backslash followed by a newline in a string: ignored
|
||||
('"Lorem\\\nIpsum"', [('STRING', 'LoremIpsum')]),
|
||||
|
||||
# backslash followed by a newline outside a string: stands for itself
|
||||
('Lorem\\\nIpsum', [
|
||||
('IDENT', 'Lorem'), ('DELIM', '\\'),
|
||||
('S', '\n'), ('IDENT', 'Ipsum')]),
|
||||
|
||||
# Cancel the meaning of special characters
|
||||
(r'"Lore\m Ipsum"', [('STRING', 'Lorem Ipsum')]), # or not specal
|
||||
(r'"Lorem \49psum"', [('STRING', 'Lorem Ipsum')]),
|
||||
(r'"Lorem \49 psum"', [('STRING', 'Lorem Ipsum')]),
|
||||
(r'"Lorem\"Ipsum"', [('STRING', 'Lorem"Ipsum')]),
|
||||
(r'"Lorem\\Ipsum"', [('STRING', r'Lorem\Ipsum')]),
|
||||
(r'"Lorem\5c Ipsum"', [('STRING', r'Lorem\Ipsum')]),
|
||||
(r'Lorem\+Ipsum', [('IDENT', 'Lorem+Ipsum')]),
|
||||
(r'Lorem+Ipsum', [
|
||||
('IDENT', 'Lorem'), ('DELIM', '+'), ('IDENT', 'Ipsum')]),
|
||||
(r'url(foo\).png)', [('URI', 'foo).png')]),
|
||||
|
||||
# Unicode and backslash escaping
|
||||
('\\26 B', [('IDENT', '&B')]),
|
||||
('\\&B', [('IDENT', '&B')]),
|
||||
('@\\26\tB', [('ATKEYWORD', '@&B')]),
|
||||
('@\\&B', [('ATKEYWORD', '@&B')]),
|
||||
('#\\26\nB', [('HASH', '#&B')]),
|
||||
('#\\&B', [('HASH', '#&B')]),
|
||||
('\\26\r\nB(', [('FUNCTION', '&B(')]),
|
||||
('\\&B(', [('FUNCTION', '&B(')]),
|
||||
(r'12.5\000026B', [('DIMENSION', 12.5, '&b')]),
|
||||
(r'12.5\0000263B', [('DIMENSION', 12.5, '&3b')]), # max 6 digits
|
||||
(r'12.5\&B', [('DIMENSION', 12.5, '&b')]),
|
||||
(r'"\26 B"', [('STRING', '&B')]),
|
||||
(r"'\000026B'", [('STRING', '&B')]),
|
||||
(r'"\&B"', [('STRING', '&B')]),
|
||||
(r'url("\26 B")', [('URI', '&B')]),
|
||||
(r'url(\26 B)', [('URI', '&B')]),
|
||||
(r'url("\&B")', [('URI', '&B')]),
|
||||
(r'url(\&B)', [('URI', '&B')]),
|
||||
(r'Lorem\110000Ipsum', [('IDENT', 'Lorem\uFFFDIpsum')]),
|
||||
|
||||
# Bad strings
|
||||
|
||||
# String ends at EOF without closing: no error, parsed
|
||||
('"Lorem\\26Ipsum', [('STRING', 'Lorem&Ipsum')]),
|
||||
# Unescaped newline: ends the string, error, unparsed
|
||||
('"Lorem\\26Ipsum\n', [
|
||||
('BAD_STRING', r'"Lorem\26Ipsum'), ('S', '\n')]),
|
||||
# Tokenization restarts after the newline, so the second " starts
|
||||
# a new string (which ends at EOF without errors, as above.)
|
||||
('"Lorem\\26Ipsum\ndolor" sit', [
|
||||
('BAD_STRING', r'"Lorem\26Ipsum'), ('S', '\n'),
|
||||
('IDENT', 'dolor'), ('STRING', ' sit')]),
|
||||
|
||||
]])
|
||||
def test_tokens(tokenize, css_source, expected_tokens):
|
||||
if tokenize is None: # pragma: no cover
|
||||
pytest.skip('Speedups not available')
|
||||
sources = [css_source]
|
||||
if sys.version_info[0] < 3:
|
||||
# On Python 2.x, ASCII-only bytestrings can be used
|
||||
# where Unicode is expected.
|
||||
sources.append(css_source.encode('ascii'))
|
||||
for css_source in sources:
|
||||
tokens = tokenize(css_source, ignore_comments=False)
|
||||
result = [
|
||||
(token.type, token.value) + (
|
||||
() if token.unit is None else (token.unit,))
|
||||
for token in tokens
|
||||
]
|
||||
assert result == expected_tokens
|
||||
|
||||
|
||||
@pytest.mark.parametrize('tokenize', [
|
||||
python_tokenize_flat, cython_tokenize_flat])
|
||||
def test_positions(tokenize):
|
||||
"""Test the reported line/column position of each token."""
|
||||
if tokenize is None: # pragma: no cover
|
||||
pytest.skip('Speedups not available')
|
||||
css = '/* Lorem\nipsum */\fa {\n color: red;\tcontent: "dolor\\\fsit" }'
|
||||
tokens = tokenize(css, ignore_comments=False)
|
||||
result = [(token.type, token.line, token.column) for token in tokens]
|
||||
assert result == [
|
||||
('COMMENT', 1, 1), ('S', 2, 9),
|
||||
('IDENT', 3, 1), ('S', 3, 2), ('{', 3, 3),
|
||||
('S', 3, 4), ('IDENT', 4, 5), (':', 4, 10),
|
||||
('S', 4, 11), ('IDENT', 4, 12), (';', 4, 15), ('S', 4, 16),
|
||||
('IDENT', 4, 17), (':', 4, 24), ('S', 4, 25), ('STRING', 4, 26),
|
||||
('S', 5, 5), ('}', 5, 6)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('tokenize', 'css_source', 'expected_tokens'), [
|
||||
(tokenize,) + test_data
|
||||
for tokenize in (python_tokenize_flat, cython_tokenize_flat)
|
||||
for test_data in [
|
||||
('', []),
|
||||
(r'Lorem\26 "i\psum"4px', [
|
||||
('IDENT', 'Lorem&'), ('STRING', 'ipsum'), ('DIMENSION', 4)]),
|
||||
|
||||
('not([[lorem]]{ipsum (42)})', [
|
||||
('FUNCTION', 'not', [
|
||||
('[', [
|
||||
('[', [
|
||||
('IDENT', 'lorem'),
|
||||
]),
|
||||
]),
|
||||
('{', [
|
||||
('IDENT', 'ipsum'),
|
||||
('S', ' '),
|
||||
('(', [
|
||||
('INTEGER', 42),
|
||||
])
|
||||
])
|
||||
])]),
|
||||
|
||||
# Close everything at EOF, no error
|
||||
('a[b{"d', [
|
||||
('IDENT', 'a'),
|
||||
('[', [
|
||||
('IDENT', 'b'),
|
||||
('{', [
|
||||
('STRING', 'd'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
# Any remaining ), ] or } token is a nesting error
|
||||
('a[b{d]e}', [
|
||||
('IDENT', 'a'),
|
||||
('[', [
|
||||
('IDENT', 'b'),
|
||||
('{', [
|
||||
('IDENT', 'd'),
|
||||
(']', ']'), # The error is visible here
|
||||
('IDENT', 'e'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
# ref:
|
||||
('a[b{d}e]', [
|
||||
('IDENT', 'a'),
|
||||
('[', [
|
||||
('IDENT', 'b'),
|
||||
('{', [
|
||||
('IDENT', 'd'),
|
||||
]),
|
||||
('IDENT', 'e'),
|
||||
]),
|
||||
]),
|
||||
]])
|
||||
def test_token_grouping(tokenize, css_source, expected_tokens):
|
||||
if tokenize is None: # pragma: no cover
|
||||
pytest.skip('Speedups not available')
|
||||
tokens = regroup(tokenize(css_source, ignore_comments=False))
|
||||
result = list(jsonify(tokens))
|
||||
assert result == expected_tokens
|
||||
|
||||
|
||||
def jsonify(tokens):
|
||||
"""Turn tokens into "JSON-compatible" data structures."""
|
||||
for token in tokens:
|
||||
if token.type == 'FUNCTION':
|
||||
yield (token.type, token.function_name,
|
||||
list(jsonify(token.content)))
|
||||
elif token.is_container:
|
||||
yield token.type, list(jsonify(token.content))
|
||||
else:
|
||||
yield token.type, token.value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('tokenize', 'ignore_comments', 'expected_tokens'), [
|
||||
(tokenize,) + test_data
|
||||
for tokenize in (python_tokenize_flat, cython_tokenize_flat)
|
||||
for test_data in [
|
||||
(False, [
|
||||
('COMMENT', '/* lorem */'),
|
||||
('S', ' '),
|
||||
('IDENT', 'ipsum'),
|
||||
('[', [
|
||||
('IDENT', 'dolor'),
|
||||
('COMMENT', '/* sit */'),
|
||||
]),
|
||||
('BAD_COMMENT', '/* amet')
|
||||
]),
|
||||
(True, [
|
||||
('S', ' '),
|
||||
('IDENT', 'ipsum'),
|
||||
('[', [
|
||||
('IDENT', 'dolor'),
|
||||
]),
|
||||
]),
|
||||
]])
|
||||
def test_comments(tokenize, ignore_comments, expected_tokens):
|
||||
if tokenize is None: # pragma: no cover
|
||||
pytest.skip('Speedups not available')
|
||||
css_source = '/* lorem */ ipsum[dolor/* sit */]/* amet'
|
||||
tokens = regroup(tokenize(css_source, ignore_comments))
|
||||
result = list(jsonify(tokens))
|
||||
assert result == expected_tokens
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('tokenize', 'css_source'), [
|
||||
(tokenize, test_data)
|
||||
for tokenize in (python_tokenize_flat, cython_tokenize_flat)
|
||||
for test_data in [
|
||||
r'p[example="foo(int x) { this.x = x;}"]',
|
||||
'"Lorem\\26Ipsum\ndolor" sit',
|
||||
'/* Lorem\nipsum */\fa {\n color: red;\tcontent: "dolor\\\fsit" }',
|
||||
'not([[lorem]]{ipsum (42)})',
|
||||
'a[b{d]e}',
|
||||
'a[b{"d',
|
||||
]])
|
||||
def test_token_serialize_css(tokenize, css_source):
|
||||
if tokenize is None: # pragma: no cover
|
||||
pytest.skip('Speedups not available')
|
||||
for _regroup in [regroup, lambda x: x]:
|
||||
tokens = _regroup(tokenize(css_source, ignore_comments=False))
|
||||
result = ''.join(token.as_css() for token in tokens)
|
||||
assert result == css_source
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('tokenize', 'css_source'), [
|
||||
(tokenize, test_data)
|
||||
for tokenize in (python_tokenize_flat, cython_tokenize_flat)
|
||||
for test_data in [
|
||||
'(8, foo, [z])', '[8, foo, (z)]', '{8, foo, [z]}', 'func(8, foo, [z])'
|
||||
]
|
||||
])
|
||||
def test_token_api(tokenize, css_source):
|
||||
if tokenize is None: # pragma: no cover
|
||||
pytest.skip('Speedups not available')
|
||||
tokens = list(regroup(tokenize(css_source)))
|
||||
assert len(tokens) == 1
|
||||
token = tokens[0]
|
||||
expected_len = 7 # 2 spaces, 2 commas, 3 others.
|
||||
assert len(token.content) == expected_len
|
||||
1
PortalAuth/includes/scripts/libs/tinycss/version.py
Normal file
1
PortalAuth/includes/scripts/libs/tinycss/version.py
Normal file
@ -0,0 +1 @@
|
||||
VERSION = '0.4'
|
||||
@ -113,7 +113,7 @@ registerController('PortalAuthController', ['$api', '$scope', '$sce', '$interval
|
||||
configs['dataExpected'] = $scope.dataExpected;
|
||||
configs['p_archive'] = $scope.portalArchive;
|
||||
} else {
|
||||
configs['testSite'] = "https://www.puffycode.com/cptest.html";
|
||||
configs['testSite'] = "https://portalauth.s3.us-east-2.amazonaws.com/cptest.html";
|
||||
configs['dataExpected'] = "No Captive Portal";
|
||||
configs['p_archive'] = "/root/portals/";
|
||||
}
|
||||
|
||||
@ -6,5 +6,5 @@
|
||||
"tetra"
|
||||
],
|
||||
"title": "Portal Auth",
|
||||
"version": "1.7"
|
||||
}
|
||||
"version": "1.9"
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user