Skip to content
Snippets Groups Projects
Commit ce99d467 authored by Hahn Axel (hahn)'s avatar Hahn Axel (hahn)
Browse files

Add features for "1.0"

server:
- allow single usage of a hash
- storing/ cleanup hashes
- prevent access to upper directories using "../"

client:
- added parameter "-l" to list items
parent 7a7945ae
No related branches found
No related tags found
No related merge requests found
<?php
$approot=dirname(__DIR__);
return array(
// define a secret aka pi key
'apikey'=>'@replace["apikey"]',
'apikey'=>'<%= @replace["apikey"] %>',
// local directory of synched ci packages
'packagedir'=>dirname(__DIR__).'/packages',
// define a secret aka api key
'apikey'=>'our-package-server-secret',
// allow directory listing when accessing a path of a package
// packages to deliver where files from ci server are synched
'packagedir'=>$approot.'/packages',
// max age of request ... client and server need to be in sync
'maxage'=>60,
// force that a hash can be used only once
// a side effect is that fast repeat or simultanius requests
// will be denied.
'onetimesecret'=>true,
// filesize of lock file with stored hashed before starting garbage collection
// 10.000 byte are reached after 114 req
'maxlockfilesize'=>50000,
// tmp dir to store used hashes
'tmpdir'=>$approot.'/tmp',
// allow directory listing when accessing a path of a package
// true is required to fetch all packages
'showdircontent'=>true,
);
<?php
$approot=dirname(__DIR__);
return array(
// define a secret aka pi key
// define a secret aka api key
'apikey'=>'our-package-server-secret',
// packages to deliver where files from ci server are synched
'packagedir'=>$approot.'/packages',
// max age of request ... client and server need to be in sync
'maxage'=>60,
// force that a hash can be used only once
// a side effect is that fast repeat or simultanius requests
// will be denied.
'onetimesecret'=>true,
// filesize of lock file with stored hashed before starting garbage collection
// 10.000 byte are reached after 114 req
'maxlockfilesize'=>10000,
// local directory of synched ci packages
'packagedir'=>dirname(__DIR__).'/packages',
// tmp dir to store used hashes
'tmpdir'=>$approot.'/tmp',
// allow directory listing when accessing a path of a package
// true is required to fetch all packages
'showdircontent'=>true,
);
\ No newline at end of file
......@@ -3,13 +3,13 @@
/**
* Check authorization in the http request header and age of timestamp
* On a failed check the request will be terminated
* @global int $iMaxAge max allowed age
* @param type $sMySecret
* @return boolean
* On a failed check the request will be terminated; on suceess it returns
* the hash
* @param string $sMySecret server api key (from config file)
* @param int $iMaxAge max allowed age in [sec]
* @return string
*/
function _checkAuth($sMySecret){
global $iMaxAge;
function _checkAuth($sMySecret, $iMaxAge=60){
$aReqHeaders=apache_request_headers();
_wd('request headers: <pre>'.print_r($aReqHeaders, 1).'</pre>');
if(!isset($aReqHeaders['Authorization'])){
......@@ -37,11 +37,63 @@ function _checkAuth($sMySecret){
$iAge=date('U')-date('U', strtotime($sGotDate));
_wd('Date: '.$sGotDate.' - age: '.$iAge.' sec');
if($iAge>$iMaxAge){
_quit('Access denied. Hash is out of date: '.$iAge. ' sec is older '.$iMaxAge.' sec. Maybe client or server is out of sync.', 403);
_quit('Access denied. Hash is out of date: '.$iAge. ' sec is older '.$iMaxAge.' sec. Maybe client or server time is out of sync.', 403);
}
if($iAge<-$iMaxAge){
_quit('Access denied. Hash is '.$iAge. ' sec in future but only '.$iMaxAge.' sec are allowed. Maybe client or server is out of sync.', 403);
}
return $sMyHash;
}
/**
* check if a given secret was used already.
* It is for hardening - allow one time usage of a hash
* @param string $lockfile filename of lockfile with stored hashes
* @param string $sMyHash hash to verify
* @return boolean
*/
function _checkIfHashWasUsedAlready($lockfile, $sMyHash){
$bFound=false;
$handle = @fopen($lockfile, "r");
if ($handle){
while (!feof($handle))
{
$buffer = fgets($handle);
if(strstr($buffer, $sMyHash)){
$bFound = true;
}
}
fclose($handle);
}
return $bFound;
}
/**
* helper function for enabled one time secret: start cleanup of storage with
* used keys if a given filesize was reached. It keeps entries younger a given
* max age (same max age like in _checkAuth()
* @param string $lockfile filename of lockfile with stored hashes
* @param integer $iMaxLockfilesize size in byte when to start garbage collection
* @param integer $iMaxAge max allowed age in [sec]
* @return boolean
*/
function _cleanupLockdata($lockfile, $iMaxLockfilesize, $iMaxAge){
if (filesize($lockfile)<$iMaxLockfilesize){
return false;
}
$sLockdata='';
$handle = @fopen($lockfile, "r");
if ($handle){
while (!feof($handle)) {
$buffer = fgets($handle);
$iTimestamp=(int)preg_replace('/\-.*$/', '', $buffer);
if ($iTimestamp && date('U') - $iTimestamp < $iMaxAge){
$sLockdata.=$buffer;
}
}
fclose($handle);
file_put_contents($lockfile, $sLockdata);
}
return true;
}
......@@ -69,7 +121,6 @@ function _quit($s, $iStatus=400){
404=>'HTTP/1.0 404 Not found',
);
header($aStatus[$iStatus]);
# _done(array('status'=>$iStatus, 'info'=>$aStatus[$iStatus], 'message'=>$s));
_sendHtml($aStatus[$iStatus], $s);
die();
}
......
......@@ -15,11 +15,12 @@
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require('../inc_functions.php');
require_once('../inc_functions.php');
$aConfig=require_once("../inc_config.php");
$iMaxAge=60;
$lockfile=$aConfig['tmpdir'].'/used_hashes.txt';
$iMaxAge=$aConfig['maxage'];
// ----------------------------------------------------------------------
// MAIN
......@@ -32,20 +33,36 @@
_wd('request uri is '.$_SERVER["REQUEST_URI"]);
_wd('<pre>GET: '.print_r($_GET, 1).'</pre>');
_checkAuth($aConfig['apikey']);
// verify hashed secret
$sMyHash=_checkAuth($aConfig['apikey'], $iMaxAge);
// if I am here then authentication was successful.
// limit to one time usage of a hash
if($aConfig['onetimesecret']){
if(_checkIfHashWasUsedAlready($lockfile, $sMyHash)) {
_quit('Access denied. The hashed was used already.');
}
_cleanupLockdata($lockfile, $aConfig['maxlockfilesize'], $iMaxAge);
// first item must be unix ts followed by "-" char ... see
// _cleanupLockdata() to detect outdated data lines
file_put_contents($lockfile, date('U').'-'.date('Y-m-d__H:i:s').'-'.$sMyHash."\n", FILE_APPEND);
}
// ---------- SPLIT URL
$sRelfile=preg_replace('#^/packages#', '', $_SERVER["REQUEST_URI"]);
_wd('$sRelfile: '.$sRelfile);
// prevent going up a directory
if (strstr($sRelfile, '..')){
_quit('Bad request. Invalid access to [..].', 400);
}
$sMyFile=$aConfig['packagedir'].$sRelfile;
_wd('full path of file: '.$sMyFile);
// handle a requested directory
if (is_dir($sMyFile)){
if(!$aConfig['showdircontent']){
_quit('Filelisting is denied by config.', 403);
......
......@@ -121,6 +121,7 @@ OPTIONS:
-d enable debug infos
-e PHASE phase; overrides env variable IMLCI_PHASE
-f FILE filename to get (without path); overrides env variable IMLCI_FILE
-l ITEM list
-o OUTFILE optional output file
-p PROJECT ci project id; overrides env variable IMLCI_PROJECT
-s SECRET override secret in IMLCI_PKG_SECRET
......@@ -135,6 +136,9 @@ VALUES:
PROJECT project id of the ci server
SECRET secret to access project data on package server. Your given secret
must match the secret on package server to get access to any url.
ITEM type what to list; one of phases|projects|files
To list projects a phase must be set.
To list files a phase and a project must be set.
DEFAULTS:
......@@ -156,11 +160,11 @@ EXAMPLES:
there is a special file ALL; it fetches all filenames by executing a directory
listing and then downloads all remote files with their original name
getfile.sh -f ''
empty file = directory listing of all your project files
getfile.sh -p ''
empty project = directory listing of all projects with current phase
getfile.sh -e preview -l projects
list existing projects in phase preview
getfile.sh -l files
list existing files of current project
Remark: The directory listing can be turned off on the package server and
results in a 403 status.
......
......@@ -22,7 +22,12 @@ bDebug=0
function showhelp(){
self=$( basename $0 )
echo "SYNTAX:
echo "
CIPGK GETTER
Get packages from a software sattelite of IML ci server.
SYNTAX:
$self [OPTIONS]
......@@ -31,6 +36,7 @@ OPTIONS:
-d enable debug infos
-e PHASE phase; overrides env variable IMLCI_PHASE
-f FILE filename to get (without path); overrides env variable IMLCI_FILE
-l ITEM list
-o OUTFILE optional output file
-p PROJECT ci project id; overrides env variable IMLCI_PROJECT
-s SECRET override secret in IMLCI_PKG_SECRET
......@@ -45,7 +51,9 @@ VALUES:
PROJECT project id of the ci server
SECRET secret to access project data on package server. Your given secret
must match the secret on package server to get access to any url.
ITEM type what to list; one of phases|projects|files
To list projects a phase must be set.
To list files a phase and a project must be set.
DEFAULTS:
You don't need to set all values by command line. Use a config to set defaults
......@@ -66,11 +74,11 @@ EXAMPLES:
there is a special file ALL; it fetches all filenames by executing a directory
listing and then downloads all remote files with their original name
$self -f ''
empty file = directory listing of all your project files
$self -p ''
empty project = directory listing of all projects with current phase
$self -e preview -l projects
list existing projects in phase preview
$self -l files
list existing files of current project
Remark: The directory listing can be turned off on the package server and
results in a 403 status.
......@@ -118,12 +126,16 @@ ${apiTS}
-X $apiMethod \
-o "${outfile}.downloading" \
--fail \
-s \
${IMLCI_URL}${apiRequest}
if [ $? -eq 0 ]; then
echo Download OK.
# echo OK.
# no outfile (= request to a directory)
if [ -z "$outfile" ]; then
echo
echo ----- RESPONSE BODY:
# echo
# echo ----- RESPONSE BODY:
cat "${outfile}.downloading"
rm -f "${outfile}.downloading"
else
......@@ -148,21 +160,37 @@ ${apiTS}
# MAIN
# ----------------------------------------------------------------------
echo
echo ===== CIPGK GETTER :: `date` =====
echo
if [ $# -lt 1 ]; then
showhelp
exit 1
fi
while getopts "de:f:o:p:s:u:" option; do
while getopts "de:f:l:o:p:s:u:" option; do
case ${option} in
d) bDebug=1 ;;
e) export IMLCI_PHASE=$OPTARG ;;
f) export IMLCI_FILE=$OPTARG ;;
l) case $OPTARG in
phases)
IMLCI_PHASE=''
IMLCI_PROJECT=''
IMLCI_FILE=''
;;
projects)
IMLCI_PROJECT=''
IMLCI_FILE=''
;;
files)
IMLCI_FILE=''
;;
*)
echo ERROR: invalid value for option [-l]
echo
showhelp
exit 2
esac
;;
o) export IMLCI_OUTFILE=$OPTARG ;;
p) export IMLCI_PROJECT=$OPTARG ;;
s) export IMLCI_PKG_SECRET=$OPTARG ;;
......@@ -200,21 +228,22 @@ if [ $bDebug = 1 ]; then
fi
if [ "$IMLCI_FILE" = "ALL" ]; then
echo ALL files were requested ...
echo Get filelist ...
tmpfilelist=/tmp/myfilelist_gdgsdgadg
# echo ALL files were requested ...
printf "%-30s" "get list of all files... "
tmpfilelist=$( mktemp )
$0 -u "${IMLCI_URL}" \
-p "${IMLCI_PROJECT}" \
-e "${IMLCI_PHASE}" \
-s "${IMLCI_PKG_SECRET}" \
-l files \
-o "${tmpfilelist}"
cat "${tmpfilelist}"
# cat "${tmpfilelist}"
cat "${tmpfilelist}" | grep "^file:" | while read fileline
do
echo $line
# echo $line
myfile=$( echo $fileline | cut -f 2- -d ':' )
echo GET $myfile...
printf "%-30s" "GET $myfile... "
$0 -u "${IMLCI_URL}" \
-p "${IMLCI_PROJECT}" \
-e "${IMLCI_PHASE}" \
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment