[Freeipa-users] REST/JSON API: Howto add a user that is not expired

Oliver Dörr oliver at doerr-privat.de
Fri Nov 13 08:43:18 UTC 2015


Hi Petr,

thanks for your hint. It works now. I've decided to change first the 
password to a random pasword as admin using user_mod and afterwards my 
code is using ipa/session/change_password to set the password to the 
final one.

So I added this to my Perl module and I'm calling the code using...
$ipaClient->changePasswordAsAdmin({'uid' =>'k812339', 'newPassword' => 
'start123', 'doNotExpire' => 'true'});

Greetings
Oliver

Am 12.11.2015 um 13:29 schrieb Petr Vobornik:
> On 11/11/2015 04:13 PM, Alexander Bokovoy wrote:
>> On Wed, 11 Nov 2015, Oliver Dörr wrote:
>>> Hi,
>>>
>>> i've tried user_mod instead because of
>>> https://docs.fedoraproject.org/en-US/Fedora/18/html/FreeIPA_Guide/pwd-expiration.html 
>>>
>>> and got
>>>
>>> Error-code:    2100
>>> Error-name:    ACIError
>>> Error-msg:    Insufficient access: Insufficient 'write' privilege to
>>> the 'krbPasswordExpiration' attribute of entry
>>> 'uid=k812339,cn=users,cn=accounts,dc=kreditwerk,dc=de'.
>>>
>>> Inside the acces log of the LDAP Server I could see...
>>>
>>> [09/Nov/2015:18:40:31 +0100] conn=658 op=7 MOD
>>> dn="uid=k812339,cn=users,cn=accounts,dc=kreditwerk,dc=de"
>>> [09/Nov/2015:18:40:31 +0100] conn=658 op=7 RESULT err=50 tag=103
>>> nentries=0 etime=0
>>>
>>> So it looks like it is a permission issue. But I still have the
>>> problem when use admin to do the job. Any idea about how to change the
>>> permission or an API that it is able to do the job?
>> You simply cannot make it working for cases when a password change
>> coming from a non-user. This is intentional.
>>
>> See http://www.freeipa.org/page/New_Passwords_Expired
>>
>> You can do double change via LDAP password change (or Kerberos) where
>> you changre a
>> password first to something temporary, then try to change it again as a
>> user with that temporary password and set a new one. Since the second
>> change would be done as a user, that should allow the change to happen
>> without raising a flag.
>
> You can use ipa/session/change_password call for that. With
>
> Content-Type:application/x-www-form-urlencoded
>
> and e.g.:
>
> user:bbar
> old_password:a
> new_password:b
>
> Web UI uses it when user with expired password is resetting his pw. So 
> you can check the communication in browser network tab.
>
>
>>>
>>> Thanks in advance
>>> Oliver
>>>
>>> Am 11.11.2015 um 15:29 schrieb Oliver Dörr:
>>>> Hi,
>>>>
>>>> i'm still working with the JSON API and I now have the problem, that
>>>> I want to add a user with a not expired password. I've tried setattr
>>>> and addattr with the following JSON code, but both fail.
>>>> {"params":[[],{"givenname":"Oliver","userpassword":"start123","uid":"k812339","version":"2.151","addattr":"krbpasswordexpiration=20160207010919Z","cn":"Oliver 
>>>>
>>>> Support","sn":"Support"}],"id":0,"method":"user_add"}
>>>>
>>>>
>>>> {"params":[[],{"givenname":"Oliver","userpassword":"start123","uid":"k812339","version":"2.151","cn":"Oliver 
>>>>
>>>> Support","setattr":"krbpasswordexpiration=20160207010919Z","sn":"Support"}],"id":0,"method":"user_add"} 
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> The user is added to IPA, but the user is still forced to change it's
>>>> password. In the response I could see that  my krbpasswordexpiration
>>>> is ignored.
>>>>
>>>> Any ideas what I'm doing wrong?
>>>>
>>>> Thanks
>>>> Oliver
>>>>
>>>
>>> -- 
>>> Manage your subscription for the Freeipa-users mailing list:
>>> https://www.redhat.com/mailman/listinfo/freeipa-users
>>> Go to http://freeipa.org for more info on the project
>>
>
>

-------------- next part --------------
package freeipa;

use strict;
use REST::Client;
use JSON;
use Encode;
use Data::Dumper;

our $VERSION="0.0.2";


BEGIN {
};


sub new
# Constructor of the class freeipa
{	my ($Class)=@_;
	my $self= {};

	bless($self,$Class);
	$self->{'id'}=0;	# id is a unique numeric identifier for the product. Red Hat uses 0.
	return $self;
}	# sub news


sub set
# Setter of the class freeipa. At this moment only usuable or scalar values 
{	my $ipaObj=shift;
	my $scalar=shift;
	my $value=shift;
	
	# Should we use the API-version from the server environment ?
	if ($scalar eq 'version' && $value eq 'env') {
		my $retObj=$ipaObj->ipaMethod({'method' => 'env'});
		if (! $retObj->{'error'} && ($retObj->{'result'}->{'result'}->{'api_version'})) { $value=$retObj->{'result'}->{'result'}->{'api_version'}; }
	}	# if ($scalar eq 'version' && $value eq 'env')

	$ipaObj->{$scalar}=$value;
}	# sub set


sub error
# This method prints out the error messages and exit 1 if configured.
{	my $ipaObj=shift;

	print "Error-code:\t".$ipaObj->{"errCode"}."\n";
	print "Error-name:\t".$ipaObj->{"errName"}."\n";
	print "Error-msg:\t".$ipaObj->{"errMsg"}."\n";
	
	if ($ipaObj->{'dieOnError'}) { exit 1; }
	
}	# sub error


sub changePasswordAsUser
# Connects to the specified IPA server via REST and changes the password of the specified user.
# You have to provide the old and the new password
{	my $ipaObj=shift;
	my $paramRef=shift;

	my $headers = {
		'Accept' => 'text/plain',
		'Content-Type' => 'application/x-www-form-urlencoded',
		'Referer' => $ipaObj->{'baseUrl'}
	};	# my $headers
	
	my $client=REST::Client->new();
	my $params = $client->buildQuery({'user' => $paramRef->{'uid'}, 'old_password' => $paramRef->{'oldPassword'}, 'new_password' => $paramRef->{'newPassword'}});
	$client->setHost($ipaObj->{'baseUrl'});
	$client->POST("/ipa/session/change_password", substr($params,1), $headers);
	
	# Change was not succesful
	if ($client->responseCode() != 200) {
		$ipaObj->{"errCode"} = $client->responseCode();
		$ipaObj->{"errName"} = "HTTP error";
		$ipaObj->{"errMsg"} = $client->responseContent();
		$ipaObj->error();
	} else {
		# Policy violations also have the responseCode == 200 and so we have to take a closer look
		if ($client->responseContent() =~ /rejected/) {
			$ipaObj->{"errCode"} = $client->responseCode();
			$ipaObj->{"errName"} = "Policy violation";
			$ipaObj->{"errMsg"} = $client->responseContent();
			$ipaObj->error();
		}	# if ($client->responseContent() =~ /rejected/)
	}	# if ($client->responseCode() != 200)
}	# sub changePasswordAsUser


sub changePasswordAsAdmin
# Connects to the specified IAP server via REST/JSON as an admin and
# sets the password of the user. You need to specifiy uid and an optional
# new password. There is a randon password generated, if you do not sepcify
# a new one. Using the option 'doNotExpire' you could get a password that
# is not expred. Please notice that the IPA  will set the new password to
# expired, if doNotExpire is not specified.
{	my $ipaClient=shift;
	my $paramRef=shift;
	my $ipaParam={};
	my $newPassword=$paramRef->{'newPassword'};
	my $oldPassword=$newPassword;

	# Set the new password either well by the caller or random 
	$ipaParam->{'uid'}=$paramRef->{'uid'};
	if ($newPassword && ! $paramRef->{'doNotExpire'}) {$ipaParam->{'userpassword'}=$newPassword} else { $ipaParam->{'random'}="true"; }
	my $retObj=$ipaClient->ipaMethod({'method' => 'user_mod'}, $ipaParam);
	if ($ipaParam->{'random'}) { $oldPassword=$retObj->{'result'}->{'result'}->{'randompassword'}; }
	
	# The password should not be expired and so we have to change it as user
	if ($paramRef->{'doNotExpire'}) {
		$ipaClient->changePasswordAsUser({'uid' =>$paramRef->{'uid'}, 'oldPassword'=>$oldPassword, 'newPassword'=>$newPassword});
	}
	return $retObj->{'result'}->{'result'}->{'randompassword'};
}	# sub changePasswordAsAdmin


sub connect
# Connects to the specified IPA server
{	my $ipaObj=shift;
	
	my $headers = {
		'Accept' => 'text/plain',
		'Content-Type' => 'application/x-www-form-urlencoded',
		'Referer' => $ipaObj->{'baseUrl'}
	};	# my $headers
	
	my $client=REST::Client->new();
	my $params = $client->buildQuery({'user' => $ipaObj->{'user'}, 'password' => $ipaObj->{'password'} });
	$client->setHost($ipaObj->{'baseUrl'});
	$client->POST("/ipa/session/login_password", substr($params,1), $headers);
	
	# Login was not succesful
	if ($client->responseCode() != 200) {
		$ipaObj->{"errCode"} = $client->responseCode();
		$ipaObj->{"errName"} = "HTTP error";
		$ipaObj->{"errMsg"} = $client->responseContent();
		$ipaObj->error();
	} else {
		$ipaObj->{'authCookie'} = $client->responseHeader('Set-Cookie');
		$ipaObj->{'restClient'}=$client;
	}	# if ($client->responseCode() != 200)
}	# sub connect


sub ipaMethod
# Calls an API method against the IPA connection
{	my $ipaObj=shift;
	my $hashRef=shift;
	my $paramRef=shift;
	
	# create the parameter hash for the API call, version will be filled by default, all others
	# came from the parameter reference that we got by calling the ipaMethod
	my $paramHash={};
	if ($ipaObj->{'version'}) { $paramHash = { 'version' => $ipaObj->{'version'} }; }
	foreach my $param (keys (%{$paramRef})) { $paramHash->{$param} = $paramRef->{$param}; }
	
	# We need data and header for the JSON request against the IPA API
	my $data = {
		'id'=>$ipaObj->{'id'},
		'params' => [
						[],
						$paramHash
					]
	};	# my $data
	
	my $headers = {
		'Accept' => 'text/plain',
		'Content-Type' => 'application/json',
		'Cookie' => $ipaObj->{'authCookie'},
		'Referer' => $ipaObj->{'baseUrl'}."/ipa/session/json"
	};
	
	# Copy the specified part of the method over the default data defined in $data
	foreach my $var (keys (%{$hashRef})) { $data->{$var}=$hashRef->{$var}; }
	
	# Format the method and data to json and make a REST request against the IPA server
	my $jsonRequest = encode_json($data);
	if ($ipaObj->{'debug'}->{'printJsonRequest'}) { print "JSON-Request: ".$jsonRequest."\n"; }
	$ipaObj->{'restClient'}->POST("/ipa/session/json", decode("utf8",$jsonRequest), $headers);
	
	# bring the JSON-formed response into perl objects...
	my $jsonReponse = $ipaObj->{'restClient'}->responseContent();
	if ($ipaObj->{'debug'}->{'printJsonResponse'}) { print "JSON-Response: ".$jsonReponse."\n"; }
	my $restRet=decode_json($jsonReponse);
	
	
	# Minimal error handling
	if ($restRet->{"error"}) {
		$ipaObj->{"errCode"} = $restRet->{"error"}->{"code"};
		$ipaObj->{"errMsg"} = $restRet->{"error"}->{'message'};
		$ipaObj->{"errName"} = $restRet->{"error"}->{'name'};
		$ipaObj->error();
	}	# if ($restRet->{"error"})
	# ... and return these
	return $restRet;
}	# sub ipaMethod


More information about the Freeipa-users mailing list