I need to get a user provider in a controller in Symfony2.
I've got multiple user_providers, with 1 chain-provider chaining those.
There is a service defined in the container with the name security.user.provider.concrete.XXX (where XXX is what you specified in security.yml), but that service is marked as private.
I've managed to define an alias in the extension class of my bundle:
$container->setAlias('my_bundle.user.provider', new Alias('security.user.provider.concrete.XXX')));
But I rather do it in a more nice way.
So, I got a couple of questions:
Is there a service I can use to fetch a specific user-provider? Which one?
If not, is there a simple way (in a pre-compiler-pass or something) to fetch all user-provider names and simply create Aliases for those services?
I've played with the configuration a little, and figured out how to generically create aliasses for any providers configured using a CompilerPass:
<?php
namespace MyBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class TestCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$securityConfig = $container->getExtensionConfig('security');
foreach ($securityConfig[0]['providers'] as $providerName => $providerConfig) {
$container->setAlias('my_security.provider.' . $providerName, new Alias('security.user.provider.concrete.' . $providerName));
}
}
}
Add the following to your Bundle class:
public function build(ContainerBuilder $container) {
// call parent
parent::build($container);
// run extra compilerPass
$container->addCompilerPass(new TestCompilerPass());
}
This will create an alias for each UserProvider that is present. It will be available under the key: my_security.provider.XXX where XXX is the name configured in your security.yml.
I'm however unsure why the config is prepended with an array with key 0.
I'm also not really sure if this is a good approach. If nothing better comes up I will be using this solution though.
You can get a specific provider by the current loggedin users firewall name:
$token = $this->securityContext->getToken();
$providerKey = $token->getProviderKey(); // secured_area / firewall name
You can get see it here: https://codedump.io/share/unA0SxVxuP9v
Related
Here is the problem.
I've got a module called Contact. It lives in a directory called modules/Contact. I registered a namespace Contact for the module in app/Config/Autoload.php. Everything seems to load fine. Now I want to use a custom validation method to validate input coming from a form field. My question is about the best way to do it. My approach is as follows: According to CI4 documentation, Validation class loads App/Config/Validation.php when used for validation. In the file there is public property called $ruleSets, which points to an array, it contains names of classes with validation rules. I want to add my own class with validation methods to this array. According to CI4 documentation on validation, configuration classes, and their public properties in App/Config/ can be updated using registrars. So, I crated one such registrar, a class, in my Contact module. It lives in Contact\Config\Registrar.php . The class has public static method Validation, which returns an array with an updated $ruleSet. The new value is an array and contains all the standard validation class names + my validation class name. The trouble is public static method Validation I added in my registrar seems not to change the value of $ruleSet defined in App/Config/. Contact/Config/Registrar.php is loaded, but Validation method it contains is not called. So, what's the best method to add custom validation rules without having to updated files which live beyond my module? There is very little on registrars in CI4 documentation. Have I misunderstood how they work and how they are supposed to be used?
I had the same problem and my solution was to extend the validation class (better solutions are always welcome).
For example I wanted to write my own Auth module and use my own set of rules. My custom validation class lives under acme/Auth/Config/AuthValidation.php
<?php
namespace Acme\Auth\Config;
use Config\Validation;
class AuthValidation extends Validation
{
public $login = [
'username' => 'required',
'password' => 'required',
];
}
This way all validation rules in your main app are inherited.
And you would use this code in your controller to validate:
<?php
namespace Acme\Auth\Controllers;
use CodeIgniter\Controller;
use Acme\Auth\Config\AuthValidation as AuthValidation;
class Auth extends Controller
{
public function login()
{
//Instantiate the new AuthValidation class
$authValidationConfig = new AuthValidation();
//Since the validation class is a config for the validation service we set it for the validation service
$authValidation = \Config\Services::validation($authValidationConfig);
//Set the login rule from our AuthValidation class
$authValidation->setRuleGroup('login');
//Validate the request against our login rule
if($authValidation->withRequest($this->request)->run())
{
return 'Success';
}
else
{
return 'Failure';
}
}
}
It may be not the best solution because I'm pretty new to CodeIgniter but this is how I found my way around it.
Sidenote: The main problem seems to be that the class passed to the validation service constructor has to be an instance of "Config\Validation" so I had to extend it. But there might be other ways around it.
Feel free to correct me :)
I have a user resource in a module that gets used by several different nodes. Now I want to add this user to a group but only in one specific node. Is there a good solution for this?
Module looks something like this:
class testmodule::basics {
user { 'testuser':
ensure => present,
home => '/home/testuser',
managehome => true,
}
}
Node manifest:
node 'testnode' {
include testmodule::basics
# here I would like to add the user to a group
# something like this (obviously does not work because of duplicate resource)
user { 'testuser':
groups => 'testgroup',
membership => 'minimum',
}
}
You have several alternatives, split among several general categories.
Category 1 - use external data to communicate which secondary groups the user should have. The particular datum might be a flag to indicate whether the user should be in the secondary group, or it might be an actual array of the appropriate secondary groups. You might then obtain it either by directly calling the lookup() or hiera() function, depending on which version of Puppet you are using, or by creating a class parameter for it, and using automatic data binding.
Example:
modules/testmodule/manifests/basics.pp:
class testmodule::basics($secondary_groups = []) {
user { 'testuser':
ensure => present,
home => '/home/testuser',
managehome => true,
groups => $secondary_groups
}
}
data/nodes/special.my.com.yaml:
---
testmodule::basics::secondary_groups:
- testgroup
Category 2 - Set up a class parameter to receive the distinguishing data, just as in one of the category 1 options, and feed the data in via an external node classifier (ENC), instead of external data. Setting up and enabling an ENC has much broader implications than feeding data to a single class, however, so I don't really recommend this unless you are already using or planning to use an ENC.
Category 3 - Perform a resource parameter override where needed. This could be almost a drop-in change to your example manifest, though it would be better to put the override in a separate class than to perform it directly in the node block. In a class that inherits from testmodule::basics, you can use resource parameter override syntax, like so:
modules/testmodule/manifests/basics/special.pp:
class testmodule::basics::special inherits testmodule::basics {
User['testuser'] {
groups => 'testgroup'
}
}
If you want to perform such an override in a node block or in an unrelated class, however, then you need to do it via a collector:
node 'testnode' {
include testmodule::basics
User<title == 'testuser'> {
groups => 'testgroup'
}
}
To two varieties of overrides have some subtle differences beyond the scopes in which they may be used, so do read the docs for more information.
I'm new to laravel; i'd like to register an object variable with key "myObject", so that it can be acceded by all users using the current application, and can be acceded in any controller or view. the object can be changed (use a set method) whenever i want. i look for something like ServletContext in JEE or somthing like registry in Zend.
Thanks in advance for you detailed code
You can bind the object into Laravel Service Container as an instance for example, you may create your object instance using something like this:
$object = new SomeClass(...);
App::instance('myObject', $myObject);
In this case, you can bind the object from a service provider. You can use the App\Providers\AppServiceProvider which comes with Laravel. So, in this case, you may do something like this:
// Import the dependencies using "use", omitted here
class AppServiceProvider extends ServiceProvider {
public function boot()
{
// ...
}
public function register()
{
$object = new MyObjectClass();
$this->app->instance('myObject', $object);
// Share the instance globally in all views
view()->share('myObject', $object);
}
}
Later, you can use $object = app('myObject') to get the instance and if you would like to use it in views globally then you may use view()->share('myObject', $object) so you can access the object directly from any view usinf $myObject instance variable. There are other ways to bind an item into the container but this approach meets your need. Check the documentation for more information.
I am trying to write a method to create a database and run migrations on it, given the connection string.
I need the multiple connections because I record an audit log in a separate database.
I get the connection strings out of app.config using code like
ConfigurationManager.ConnectionStrings["Master"].ConnectionString;
The code works with the first connection string defined in my app.config but not others, which leads me to think that somehow it is getting the connection string from app.config in some manner I don't know.
My code to create the database if it does not exist is
private static Context MyCreateContext(string ConnectionString)
{
// put the connection string where the factory method can get it
AppDomain.CurrentDomain.SetData("ConnectionString", ConnectionString );
var factory = new ContextFactory();
// I know I need this line - but I cant see how what follows actually uses it
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context,DataLayer.Migrations.Configuration>());
var context = factory.Create();
context.Database.CreateIfNotExists();
return context
}
The code in the Migrations.Configuration is
Public sealed class Configuration : DbMigrationsConfiguration<DataLayer.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
The context factory code is
public class ContextFactory : IDbContextFactory<Context>
{
public Context Create()
{
var s = (string)AppDomain.CurrentDomain.GetData("ConnectionString");
return new Context(s);
}
}
Thus I am setting the connection string before creating the context.
Where can I be going wrong, given that the connection strings are all the same except the database name, and the migration code runs with one connection string, but doesnt run with others?
I wonder if my problem is to do with understanding how How does Database.SetInitializer actually works. I am guessing something about reflection or generics. How do i make the call to SetInitializer tie tie to my actual context?
I have tried the following code but the migrations do not run
private static Context MyCreateContext(string ConnectionString)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, DataLayer.Migrations.Configuration>());
var context = new Context(ConnectionString);
context.Database.CreateIfNotExists();
}
This question appears to be related
UPDATE:
I can get the migrations working if I refer to the connection string using
public MyContext() : base("MyContextConnection") - which points to in the config
I was also able to get migrations working on using different instances of the context, if I created a ContextFactory class and passed the connection to it by referencing a global. ( See my answer to the related question link )
Now I am wondering why it has to be so hard.
I'm not sure exactly as to what the problems are you facing, but let me try
The easiest way to provide connection - and be sure it works that way...
1) Use your 'DbContext' class name - and define a connection in the app.config (or web.config). That's easiest, you should have a connection there that matches your context class name,
2) If you put it into the DbContext via constructor - then be consistent and use that one. I'd also suggest to 'read' from config connections - and again name it 'the same' as your context class (use the connection 'name', not the actual string),
3) if none is present - EF/CF makes the 'default' one - based on your provider - and your context's class name - which usually isn't what you want,
You shouldn't customize with initializers for that reason -
initializers should be agnostic and serve other purpose - setup
connection in the .config - or directly on your DbContext
Also check this Entity Framework Code First - How do I tell my app to NOW use the production database once development is complete instead of creating a local db?
Always check 'where your data' goes - before doing anything.
For how the initializer actually works - check this other post of mine, I made a thorough example
How to create initializer to create and migrate mysql database?
Notes: (from the comments)
Connection shouldn't be very dynamic - config is the right place for it to be, unless you have a good reason.
Constructor should work fine too.
CreateDbIfNotExists doesn't work well together with the 'migration' initializer. You can just use the MigrateDatabaseToLatestVersion initializer. Don't 'mix' it
Or - put something like public MyContext() : base("MyContextConnection") - which points to <connectionStrings> in the config
To point to connection - just use its 'name' and put that into constructor.
Or use somehting like ConfigurationManager.ConnectionStrings["CommentsContext"].ConnectionString
Regarding entertaining 'multiple databases' with migrations (local and remote from one app) - not exactly related - but this link - Migration not working as I wish... Asp.net EntityFramework
Update:
(further discussion here - Is adding a class that inherits from something a violation of the solid principles if it changes the behavior of code?)
It is getting interesting here. I did manage to reproduce the problems you're facing actually. Here is a short breakdown on what I think it's happening:
First, this worked 'happily':
Database.SetInitializer(new CreateAndMigrateDatabaseInitializer<MyContext, MyProject.Migrations.Configuration>());
for (var flip = false; true; flip = !flip)
{
using (var db = new MyContext(flip ? "Name=MyContext" : "Name=OtherContext"))
{
// insert some records...
db.SaveChanges();
}
}
(I used custom initializer from my other post, which controls migration/creation 'manually')
That worked fine w/o an Initializer. Once I switched that on, I ran into some curious problems.
I deleted Db-s (two, for each connection). I expected to either not work, or create one db, then another in the next pass (like it did, w/o migrations, just 'Create' initializer).
What happened, to my surprise - is it actually created both databases on the first
pass ??
Then, being a curious person:), I put breakpoints on the MyContext ctor, and debugged through the migrator/initializer. Again empty/no db-s etc.
It created first instance on my call within the flip. Then on the first access to 'model', it invoked the initializer. Migrator took over (having had no db-s). During the migrator.Update(); it actually constructs the MyContext (I'm guessing via generic param in Configuration) - and calls the 'default' empty ctor. That had the 'other connection/name' by default - and creates the other Db all as well.
So, I think this explains what you're experiencing. And why you had to create the 'Factory' to support the Context creation. That seems to be the only way. And setting some 'AppDomain' wide 'connection string' (which you did well actually) which isn't 'overriden' by default ctor call.
Solution that I see is - you just need to run everything through factory - and 'flip' connections in there (no need for static connection, as long as your factory is a singleton.
You can supply a configuration in the MigrateDatabaseToLatestVersion constructor.
If you set the initializer in the DbContext you can also pass a 'true' to use the current connection string.
I have generated ServiceContext for my CRM organization. I'm able to connect to CRM properly. Since I have all my context configuration in app.config file, I wonder is it possible to get IOrganizationService from already instantiated OrganizationServiceContext?
When I need to access the service reference from multiple places, I usually do it in two steps. First of all I try to see if it's possible to pass it down to the called methods (I'm assuming that you have something like the following).
using (IOrganizationService service
= (IOrganizationService) new OrganizationServiceProxy(...))
{
DoSomething();
DoSomething(service);
}
private void DoSomething(IOrganizationService service) { ... }
When it fails (due to technical setup or just plain dumbness), I set up a private property and in the constructor (or at least the first calling method) assign it a value for future access like this.
class MyClass
{
private IOrganization _service;
private IOrganization _Service
{
get
{
if(_service == null)
_service = (IOrganizationService) new OrganizationServiceProxy(...);
return _service;
}
}
...
}
And if you have a lot of code that operates on the server, you might want to move all that stuff to a separate class and have the calls made to it (with the property setup discussed above).
I'm not fully sure if I got your question correctly so be nice if I'm missing your point.