PHP provides a great function for copying or moving directory records within LDAP. Unfortunately this ldap_rename function doesn't seem to work on the Sun Directory. Do any alternatives exist to change an account's OU without having to create a new account?
My end goal is to have a simple method to switch between two OU's, such as:
CN=username,OU=Admin,DC=uaa,DC=alaska,DC=edu to
CN=username,OU=Student,DC=uaa,DC=alaska,DC=edu
You can do it with LDIF. On the directory point of view, the job you want to do is called a DN modification, there are two LDAP verbs for that moddn and modrdn.
It can be done in LDIF by this way in OpenLDAP:
dn: CN=username,OU=Admin,DC=uaa,DC=alaska,DC=edu
changetype: modrdn
newrdn: CN=username
deleteoldrdn: 0
newsuperior: OU=Student,DC=uaa,DC=alaska,DC=edu
I use this way accros Active Directory :
dn: CN=username,OU=Admin,DC=uaa,DC=alaska,DC=edu
Changetype: moddn
Newrdn: CN=username
Deleteoldrdn: 1
Newsuperior: OU=Student,DC=uaa,DC=alaska,DC=edu
Be careful, copy/delete is significantly different from moddn and modrdn in the first solution you create new objects (new guid or uuid in the LDAP database) and it can impact replication. In the second solution you move objects.
Perhaps you can find there verbs in PHP.
Related
I'm trying to make auth calls connecting from php to an Active Directory server auth looks fine but I don't know what to put as ldap_search parameters.
dump of ldap_connect:
resource(4) of type (ldap link)
dump of ldap_bind:
bool(true)
dump of ldap_search:
resource(5) of type (ldap result)
dump of ldap_get_entries:
array(1) {
["count"]=>
int(0)
}
I tried an endless number of permutation of this kind of parameters:
$ldap_dn = "CN=Users,DC=ad,DC=domain";
$ldap_filter = "(objectClass=*)";
but I'm not sure what exactly to put as DC= value in my specific case or O= or OU= or CN= or whatever, any help will be appreciated.
The base distinguished name is the base distinguished name you'd like to perform operations on. An example base DN would be DC=corp,DC=acme,DC=org.
You can learn it from your active directory server manager.
I also recommend you to use adLDAP Package
It is a PHP class that provides LDAP authentication and integration with Active Directory.
If one dc is not defined, then the package will try to find it automatically by querying your server. It's recommended to include it to limit queries executed per request. It has some custom functions that use PHP's defualt ones. Hence, using the package may make your work easier.
I've managed to get a stable load balanced front end servers that can scale horizontally quite well however the next bottle neck would be the db. There was a blog post discussing scaling dbs horizontally however very little detail on it. I'm currently using PostgreSQL and so the only plugin I've found wouldn't work.
Are my only options creating my own HAProxy or rewriting the PostgreSQL plugin to allow connections with read replicas?
I'm using AWS for all my hosting
Firstly - I'd love to be corrected on this!
Having only had a quick look through some of the ORM classes in a SilverStripe 3.5 site, it looks like while the ORM does support multiple database connections (see DB::get_conn with argument for name) it is designed for specific use cases in mind. That is to say, you may have a module that needs to write to a specific database, so this would allow it to.
What you want is native and automatic support for this within the framework, so that all reads go to your slave(s) and writes go to your master. Unfortunately, it doesn't look like this comes out of the box. You might be able to achieve it by overloading a couple of the core SQL classes using the injector.
If you were to try it, this answer outlines how you could separate select statements out from the rest and run them through a different database connector.
As a quick example of how you might go at achieving this with SQLSelect, you will notice that it is injectable, which means you can easily overload it.
File: mysite/_config/injector.yml
Injector:
SQLSelect:
class: ReadOnlySQLSelect
You need to register a new database connection with the DB class:
File: mysite/_config.php
$readDatabaseConfig = array(/** define your DB credentials here, as with the default $databaseConfig **/);
if (!DB::connect($readDatabaseConfig, 'default_read')) {
user_error('Failed to connect to read replica DB!', E_USER_ERROR);
}
Now, overload the SQLSelect class and replace the parts of it that call the DB class methods. This class inherits from SQLExpression which is the class the contains the methods you actually care about in this instance:
File: mysite/code/ReadOnlySQLSelect.php
class ReadOnlySQLSelect extends SQLSelect
{
public function sql(&$parameters = array())
{
// Changed from SQLExpression: third parameter passed as connection name
$sql = DB::build_sql($this, $parameters, 'default_read');
if (empty($sql)) {
return null;
}
if ($this->replacementsOld) {
$sql = str_replace($this->replacementsOld, $this->replacementsNew, $sql);
}
return $sql;
}
public function execute()
{
$sql = $this->sql($parameters);
// Changed from SQLExpression: skip DB::prepared_query since it doesn't allow
// you to provide the connection name - replace it with its contents instead.
$conn = DB::get_conn('default_read');
return $conn->preparedQuery($sql, $parameters);
}
}
Note: SQLSelect::unlimitedRowCount should technically be replaced where it calls DB::prepared_query, since the prepared query method calls DB::get_conn with no arguments, so will always return the default connection. You could replace the DB::prepared_query line the same as used above:
$conn = DB::get_conn('default_read');
$result = $conn->preparedQuery($sql, $innerParameters);
If you implement the above method, also change new SQLSelect() to SQLSelect::create(), otherwise you'll end up with some queries that still hit the master server because it'll bypass your class by not using the injector.
There's also an instance in SQLConditionalExpression that you should replace too (::toSelect) but that is likely to affect query transformations from other child implementations of that class, and you won't be able to do much about it without either (A) PRing a fix to the framework or (B) overloading all the other SQL* classes.
At this point you should have everything you need to route select queries to your default_read connection.
Infrastructure
On the infrastructure side, you should be able to set up read replicas through the RDS console. When you do so it will provide you with a DNS endpoint for your replica node(s), which you can use in your _config.php to configure the connection to the read replica database.
If this works for you, you should create a module for it and put it up on GitHub - this would definitely be useful for others in future!
You may also consider making pull requests to the framework to add additional arguments to methods like DB::prepared_query to accept a connection name.
Also worth noting is that if you're using the mysqlnd database adapter you may be able to take advantage of read/write splitting, implemented with some sort of injector overloading but all handled at a lower level than the application layer.
I have, let's say, five types of job A,B,C,D,E. Each has different configuration. I want to make a configuration system in PHP, which is solely in PHP. In this system I like to return an object of configuration on specifying job type:
$obj=new ServiceConfiguration(); $configForA= $obj->getConfig('A');
getConfig reads the config, which may contain URL, job server, port and set this in config object. In writing all this I want that proper design should be followed.
Should I write all config parameter in a PHP array in a config file or there is any better alternative to it? Can I use external parties like YAML to store config? I want to know the best way to achieve it. Please answer with the whole design.
NOTE: I want to write a third party which should be written in PHP and should have no or minimal dependency on external third parties like YAML.
I personnaly use JSON files for configuration and a PHP class to read thoses files.
Accessing a var is doing by giving a string with underscores, that way I can explore the depth of the JSON file
example :
database.json
{
"prod" :
{
"server" : "127.0.0.1"
},
"key_with_underscore" : "value"
}
script.php
Config::get('database_prod_server');
Config::get('database_key_with_underscore');
I also do some small treatment to access JSON array as PHP array but basically, that's it.
In fact the config class just transform a multidimensionnal array to one-dimensionnal array by flattening it and insert an underscore for each new "dimension", then I stock the result in a variable (and cache it).
So The Direct Project strikes again. I'm no expert in LDAP, but I'm trying to set up a test environment since the standard requires any package to support getting certificates from LDAP as well as DNS CERT, regardless of which method is implemented by the package.
According to the documentation, the prescribed sequence of events (trimmed for relevance) from section "3.3.3 LDAP query":
* Discover the Base DNs
Branches in LDAP must be defined by a “Base DN”. The list of Base DNs that are
provided by a LDAP directory are found by doing a LDAP Query with a NULL (i.e.
“”) Base DN, and ObjectClass=”DN”.
* Query across the Base DN for entries where "Mail" contains the endpoint address
I'm trying to implement this process in php, using the ldap_* functions, but their way doesn't seem to work. Obviously, NULL is not the same as an empty string (the latter makes any call to ldap_search return a "No such object" error), and "DN" isn't a valid value for an ObjectClass attribute.
So, TL;DR, is there another way an anonymous remote user retrieve the (list of?) base DNs that I'm missing?
UPDATE: Reworded the title to reflect the root cause of my problem: Reading the rootDSE from PHP when the ldap_* api doesn't allow you to specify 'base' scope.
So another read through the docs answered my question for me.
Apparently, the only difference between ldap_search(), ldap_list(), and ldap_read() are the scopes (LDAP_SCOPE_SUBTREE (sub), LDAP_SCOPE_ONELEVEL (one), and LDAP_SCOPE_BASE (base), respectively). So using ldap_read() instead of the others will allow one to get the rootDSE.
In the root dse. See "namingContexts".
Update:
In java:
LDAPConnection conn = new LDAPConnection(hostname,port);
SearchRequest req = new SearchRequest("",SearchScope.BASE,"(&)","+");
SearchResult result = conn.search(req);
// If the search succeeds, the result will comprise one entry,
// and that entry is the Root DSE:
dn:
subschemaSubentry: cn=schema
namingContexts: C=us
vendorName: UnboundID Corp.
vendorVersion: UnboundID Directory Server 4.1.0.6
PHP's Mongo driver lacks a renameCommand function. There is reference to do this through the admin database. But it seems more recent versions of the Mongo driver don't let you just "use" the admin database if do don't have login privileges on that database. So this method no longer works. I've also read this doesn't work in sharded environments although this isn't a concern for me currently.
The other suggestion people seem to have is to iterate through the "from" collection and insert into the "to" collection. With the proper WriteConcern (fire and forget) this could be fairly fast. But it still means pulling down each record over the network into the PHP process and then uploading it back over the network back into the database.
I ideally want a way to do it all server-side. Sort of like an INSERT INTO ... SELECT ... in SQL. This way it is fast, network efficient and a low load on PHP.
I have just tested this, it works as designed ( http://docs.mongodb.org/manual/reference/command/renameCollection/ ):
$mongo->admin->command(array('renameCollection'=>'ns.user','to'=>'ns.e'));
That is how you rename an unsharded collection. One problem with MR is that it will change the shape of the output from the original collection. As such it is not very good at copying a collection. You would be better off copying it manually if your collection is sharded.
As an added note I upgraded to 1.4.2 (which for some reason comes out from the pecl channel into phpinfo() as 1.4.3dev :S) and it still works.
Updates:
Removed my old map/reduce method since I found out (and Sammaye pointed out) that this changes the structure
Made my exec version secondary since I found out how to do it with renameCollection.
I believe I have found a solution. It appears some versions of the PHP driver will auth against the admin database even though it doesn't need to. But there is a workaround where the authSource connection param is used to change this behavior so it doesn't auth against the admin database but instead the database of your choice. So now my renameCollection function is just a wrapper around the renameCollection command again.
The key is to add authSource when connecting. In the below code $_ENV['MONGO_URI'] holds my connection string and default_database_name() returns the name of the database I want to auth against.
$class = 'MongoClient';
if( !class_exists($class) ) $class = 'Mongo';
$db_server = new $class($_ENV['MONGO_URI'].'?authSource='.default_database_name());
Here is my older version that used eval which should also work although some environments don't allow you to eval (MongoLab gives you a crippled setup unless you have a dedicated system). But if you are running in a sharded environment this seems like a reasonable solution.
function renameCollection($old_name, $new_name) {
db()->$new_name->drop();
$copy = "function() {db.$old_name.find().forEach(function(d) {db.$new_name.insert(d)})}";
db()->execute($copy);
db()->$old_name->drop();
}
you can use this. "dropTarget" flag is true then delete exist database.
$mongo = new MongoClient('_MONGODB_HOST_URL_');
$query = array("renameCollection" => "Database.OldName", "to" => "Database.NewName", "dropTarget" => "true");
$mongo->admin->command($query);