LDAP SSL connection/bind with self signed certificate using XAMPP/WAMP on Windows

I spent a lot of time figuring out how to configure force LDAP PHP module to accept self signed certifactes on windows with XAMPP and WAMP servers.

Here is my configuration:

  •  Windows XP Professional SP3
  •  ApacheFriends XAMPP version 1.7.7
  • Apache 2.2.21
  • MySQL 5.5.16 (Community Server)
  • PHP 5.3.8 (VC9 X86 32bit thread safe) + PEAR
  • OpenSSL 1.0.0e
  •  WAMP version 2.2a x32
  • Apache 2.2.21
  • MySQL 5.5.16
  • PHP 5.3.8
  • OpenSSL – don’t know version

I started with WAMP server, then I switched to XAMPP with a hope that it will simply work. It didn’t, however, I’ve found solution for XAMPP which should also work for WAMP.

First and foremost, uncommenting php_ldap.dll in php.ini caused problem with staring apache at all. There are some problems with openssl libs. I copied XAMPP/php/bin/libsasl.dlls to XAMPP/apache/bin/ and then in the other way XAMPP/apache/bin/libeay32.dll and ssleay32.dll to XAMPP/php/bin/. After this operation apache was able to start.

Then, I wanted to establish connection with LDAP over SSL and the server is using self signed certificate. Note, that using SSL the connection is not established when calling ldap_connect(), but later when calling ldap_bind(). I was getting following error:

Unable to bind to server: Can't contact LDAP server

You can also turn on debugging for ldap module with following code:

ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);

and then you’ll see following error in apache’s error.log:

TLS certificate verification: Error, self signed certificate in certificate chain

I’ve found that to allow self signed certificates you need to create ldap.conf with configuration option:

TLS_REQCERT never

Ok, but where this file should be located? I have found information that it should be root directory on C:\ or on D:\. There were also information that apache or even windows restart is needed. None of these was working for me.

Finally, I’ve found that my location for ldap.conf is C:\openldap\sysconf\ and only apache restart was needed to make it work.

 

 

Advertisements

Leave a comment

How to create exe for java program

I always had the same problem with java programs packed with jar. How to run them in friendly manner. The best example of this can be eclipse which is run from eclipse.exe but it is written entirely in java.

I search a lot for some program which would make this easy and I found it. It’s called JSmooth.

So I had my application called Ndsr in jar file ndsr.jar

First we need to run JSmooth.

Then go to tab Skeleton and choose Windowed wrapper

Put message which will be shown to user when appropriate JVM is not found on user’s machine.

Also check “Launch java app in the exe process.

Save configuration in folder where you have your application jar

Next go to application tab and click icon with ‘+’ to add this jar.

Then click on ‘…’ button next to Main class text field to choose main class (this should be done automatically if you have main class defined in manifest but isn’t)

Choose your main class from ‘Available Classes’ and click select

Go to Executable tab and put name of your application with exe extension

You can also choose icon but there are some problems with .ico files (another bug) and png files are very ugly resized.

I next post I will describe how to change/add icon in exe files.

Then we need to specify JVM version so we need to go to JVM Section tab

Put version in Minimum JVM Version field.

If you need any specific JVM arguments go to JVM Configuration tab

So we are ready to go. Just click icon with gear or go to Project > Compile in menu.

, , , ,

1 Comment

Chrome application redirection loop problem

Recently I tried to pack my game http://qfel13.github.com/xit/ into chrome app. To enable offline playing in my game. When I did this there was problem with redirection to level editor.

Chrome communicated that there is redirection loop.

I checked my code of redirection and there was something like this:

window.href = '/editor/#load';

The problem was that href didn’t point to actual page so ‘/editor/index.html’ but only to folder for which webserver automatically served index.html file.

The same problem was with redirection from editor to game

window.href = '../#load';

Needed to be changed to:

window.href = '../index.html#load';

Everything started to work well after this changes. You can find game app here.

, , ,

Leave a comment

Uploading file in Yii using CUploadedFile

This article explains how to implement file uploading in Yii framework. Let’s start with basics. First, we need to have the form with multipart/form-data encoding and input field of file type.

$form = $this->beginWidget('GxActiveForm', array(
	'id' => 'form-id',
	'enableAjaxValidation' => false,
	'htmlOptions' => array(
		'enctype' => 'multipart/form-data',
	),
));
...
<?php echo $form->fileField($model, 'filename', array('size' => 48)); ?>	

Note! I’m using GxActiveForm instead of CActiveForm because to generate CRUD I’m using giix module which is enhancement of standard gii generator.

Secondly, in the model we need to add file validator which can check the file size or extension.

	public function rules()
	{
		return array_merge(parent::rules(), array(
				array('filename', 
						'file',
						'allowEmpty' => true,
						// 'types'=>'jpg, gif, png',
						'maxSize'=>1024 * 1024 * 50, // 50MB
						'tooLarge'=>'The file was larger than 50MB. Please upload a smaller file.',
				),
			)
		);
	}	

Again, I’m using giix extension which generates base model classes and classes which extends them. Therefore to change default behavior of base class I’m just changing only some values returned by parent class. Please also note that setting ‘maxSize’ may not be sufficient. You also need to set configuration variables in php.ini (upload_max_filesize, post_max_size, memory_limit, etc.)

At the end we also need to add some code to controller (create and update actions). Let’s consider actionUpdate().

$oldfilename = $model->filename; // let's store original filename (if it was defined)
$model->setAttributes($_POST['SomeModel']);

// Now check if there was anything uploaded and store new name if so
$file = CUploadedFile::getInstance($model, 'filename');
if (is_object($file) && get_class($file)==='CUploadedFile') {			
    $model->filename = $file;
} else {
    $model->filename = $oldfilename;
}	

if ($model->save()) {
    if (is_object($file) && get_class($file)==='CUploadedFile') {
        // again, if anything was uploaded and if we have db done then move the file from tmp to the right place
        $model->filename->saveAs(Yii::app()->basePath . '/files/' . $file->filename->name);
        if ($oldfilename != $model->filename->name) {
            unlink($model->fileWithPath(Yii::app()->basePath . '/files/' . $oldfilename));
        }
    }					
    $this->redirect(array('view', 'id' => $model->id));
}

Ok, but still there are some problems. First and foremost, we don’t have a way to remove already added file (we can only replace it with new one). Secondly, if we need to have some access restrictions to uploaded files then we need to randomize their names and have them available via some dispatching php script.

To delete file we will add checkbox to indicate that the file should be deleted, so in the view add following code under fileField:

<?php if ($this->getAction()->getId() == 'update' && strlen($model->filename) > 0) { ?>
    <br /><?php echo CHtml::checkBox('delfile'); ?> check to delete file <?php echo $model->fileLink(); ?>
<?php  } ?>

At the same time new method fileLink() is added to the model to provide link to the action which will provide file itself (and where we will be able to add some access restrictions) and we are adding file deletion methods:

    public function fileLink() {
    	return CHtml::link($this->filename, CHtml::normalizeUrl(array('file', 'id' => $this->id)));
    }

    public function deleteFile() {
    	if (strlen($this->filename) > 0 && file_exists($this->fileWithPath()) && !is_dir($this->fileWithPath())) {
    		unlink($this->fileWithPath());
    	}
    }
    
    protected function afterDelete() {
    	parent::afterDelete();
    	$this->deleteFile();
    }

Now, in the controller we need to add support for file deletion (on update action only, because when calling $model->delete() the above method afterDelete() will be called and this will call file deletion). So, to simplify:

if (isset($_POST['delfile'])) {
    $model->deleteFile();
}

and at the end the action which will provide the file itself:

	public function actionFile($id) {
		$model = $this->loadModel($id, 'So');	
		if (file_exists($model->fileWithPath())) {
			header("Pragma: no-cache");
			header("Expires: 0");
			header('Content-Description: File Transfer');
			header('Content-Type: ' . CFileHelper::getMimeType($model->fileWithPath()));
			header('Content-Disposition: attachment; filename="'.$model->filename.'"');
			header('Content-Transfer-Encoding: binary');
			header('Expires: 0');
			header('Cache-Control: must-revalidate');
			header('Pragma: public');
			header('Content-Length: ' . filesize($model->fileWithPath()));		
			readfile($model->fileWithPath());			
			Yii::app()->end();
		} else {
			throw new CHttpException(404, 'Not found');
		}
	}

5 Comments

How to customize appearance of CJuiAutoComplete in Yii

Before we go to the point of this post let’s start with some basics. It is quite easy to use CJuiAutoComplete widget in your form. Basically you have to add the widget to your form:


$form->widget('zii.widgets.jui.CJuiAutoComplete', array(
  'name' => 'field_name',
  'value' => $model->field_name,
  'sourceUrl' => array('some_view/autoComplete'),
  'options'=>array(
  'minLength' => '2',
  'showAnim' => 'fold',
  'select' => "js: function(event, data) {
    alert(data.item['id']);
  }"
)));

This widget will use ‘sourceUrl’ (ie. autoComplete action of some_view) as a source of possible option to choose from. The function defined in ‘select’ parameter will be triggered when user chooses a value from the list. Other options are rather self descriptive and less important. More info you can find in yii and jquery documentation.

Now, the widget expects that data returned by the action is in JSON format. Here is an example of such action:

public function actionAutoComplete($term) {
  $retVal = array();

  if (strlen($term) >= 2) {
    $model = Wbs::model();

    $criteria = new CDbCriteria();
    $criteria->compare('some_field', $term, true);
    $criteria->compare('other_field', $term, true, 'OR');
    $criteria->compare('another_field', $term, true, 'OR');

    $criteria->limit = 10;

    foreach($model->findAll($criteria) as $item) {
      $retVal[] = array(
        'label' => $item->some_field,
        'value' => $item->some_field,
        'id' => $item->id,
        'other_field' => $item->other_field,
        'another_field' => $item->another_field,
     );
   }
 }

 echo CJSON::encode($retVal);
 Yii::app()->end();
}

Now we’re going to the point. It’s plain to see that it is not a problem to add some other fields to the query, but how to show some more values on the list of autocomplete? In this case jquery documentation can be very helpful (while yii’s docs says nothing about it). What you need to do is add method (or rather replace default one) which renders each element on a list of possible values:

jQuery('#field_name').data('autocomplete')._renderItem = function( ul, item ) {
  return $('<li></li>')
    .data('item.autocomplete', item)
    .append('<a>' + item.some_value + '<br><i>' + item.other_value + '</i></a>')
    .appendTo(ul);
};

I think that it’s easy to see in the above code that the “item” is one element of JSON array returned from autoComplete action and in the function you can use its values.

And there are some important things. First and foremost, ‘#field_name’ has to match the name which is used when CJuiAutoComplete widget is created. Secondly, this javascript code has to be executed when you are sure that this autocomplete element is already initialized in the document! To do this you can use yii’s registerScript() method with CClientScript::POS_READY parameter.

Yii::app()->clientScript->registerScript('autocomplete', "
  jQuery('#field_name').data('autocomplete')._renderItem = function( ul, item ) {
    return $('<li></li>')
      .data('item.autocomplete', item)
      .append('<a>' + item.some_value + '<br><i>' + item.another_value + '</i></a>')
      .appendTo(ul);
  };",
  CClientScript::POS_READY
);

, ,

5 Comments