adro

This user hasn't shared any biographical information

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

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