Dynamic database connection in Symfony - symfony

I have a second entity manager, and i want to get parameters from a database and change the DATABASE_URL to connect to another database.
# config/packages/doctrine.yaml
my_entity:
url: '%DATABASE_URL%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
I try to add a parameter from the process function from Kernel.php, but it's apparently run only one time.

This can be done by changing the data of the Connection class. You need to create your own Connection class and hook it up to the project.
Let's create the required class Connection
// src/Doctrine/DatabaseConnection.php
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
class DatabaseConnection extends Connection
{
public function __construct(array $params, Driver $driver, $config, $eventManager )
{
// First, let symfony connect to the default database from env so that a Connection instance appears in order to execute a sql query to get the necessary data
parent::__construct($params, $driver, $config, $eventManager);
// Getting data and changing it
$user_database = $this->executeQuery('SELECT * FROM database WHERE user_id = :user_id',['user_id'=>1])->fetch();
$params['dbname'] = $user_database['dbname'];
$params['user'] = $user_database['user'];
$params['password'] = $user_database['password'];
parent::__construct($params, $driver, $config, $eventManager);
}
}
Note: As you can see in the example, to begin with, we connect to our main database from .env so that we can access the desired database by executing an sql query. After receiving the data, we modify it and call the parent's constructor method again to replace the Connection instance with the database we want.
Initialize DatabaseConnection
# config/packages/doctrine.yaml
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
wrapper_class: App\Doctrine\DatabaseConnection
Note: Here we have connected our handler using the DBAL wrapperClass option

Related

AWS AmazonSimpleSystemsManagementClient cannot read credentials in .NET Framework application

I have .NET Framework application where I try to read data from AWS parameter store using AmazonSimpleSystemsManagementClient on my local environment. Besides I have credentials generated by AWS CLI and located in
Users/MyUser/.aws
folder. When I try to connect to the parameter store from CMD using the creds it works fine. Though the AmazonSimpleSystemsManagementClient in the application with default constructor, it throws exception "Unable to get IAM security credentials from EC2 Instance Metadata Service." When I tried to pass BasicAWSParameters to the client with hardcoded working keys I got another exception "The security token included in the request is invalid".
Also I tried installing EC2Config, initializing AWS SDK Store from Visual Studio AWS Toolkit. Though it didn't change the game.
I would want to avoid using environment variables or hardcoding the keys since keys are generated and valid only 1 hour. Then I should regenerate so copying them somewhere every time is not convenient for me.
Please advice how to resolve the issue.
Some code
_client = new AmazonSimpleSystemsManagementClient()
public string GetValue(string key)
{
if (_client == null)
return null;
var request = new GetParameterRequest
{
Name = $"{_baseParameterPath}/{key}",
WithDecryption = true,
};
try
{
var response = _client.GetParameterAsync(request).Result;
return response.Parameter.Value;
}
catch (Exception exc)
{
return null;
}
}
credentials file looks as following (I removed key values not to expose):
[default]
aws_access_key_id= KEY VALUE
aws_secret_access_key= KEY VALUE
aws_session_token= KEY VALUE
[MyProfile]
aws_access_key_id= KEY VALUE
aws_secret_access_key= KEY VALUE
aws_session_token= KEY VALUE
As long as you have your creds in .aws/credentials, you can create the Service client and the creds will be located and used. No need to create a BasicAWSParameters object.
Creds in a file named credentials:
[default]
aws_access_key_id=Axxxxxxxxxxxxxxxxxxxxxxxxxxx
aws_secret_access_key=/zxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
This .NET code works.
using System;
using System.Threading.Tasks;
using Amazon.SimpleSystemsManagement;
using Amazon.SimpleSystemsManagement.Model;
namespace ConsoleApp1 {
class Program {
static async Task Main(string[] args) {
var client = new AmazonSimpleSystemsManagementClient();
var request = new GetParameterRequest()
{
Name = "RDSConnection"
};
var response = client.GetParameterAsync(request).GetAwaiter().GetResult();
Console.WriteLine("Parameter value is " + response.Parameter.Value);
}
}
}

Getting a One-to-One Relation to work in Azure Appservice using EF Code First

I am having a hard time getting this to work, so maybe you can help me out here.
Technologies used
Xamarin.Forms
Azure App Service
Azure SQL
AzureMobileServices (MobileServiceClient, MobileServer)
Entity Framework Code First
Introduction
My Xamarin.Forms app is used by users with both a billing and a delivery address. The database scheme looks like this:
Users
Id
BillingAddress_Id
DeliveryAddress_Id
Addresses
Id
User_Id
When adding new addresses, I basically do something like that:
var user = await userService.GetUserByIdAsync(...);
var billingAddress = new Address
{
UserId = user.Id,
…
};
var deliveryAddress = new Address
{
UserId = user.Id,
…
}
user.BillingAddress = billingAddress;
user.DeliveryAddress = deliveryAddress;
await addressService.AddNewAddressAsync(billingAddress);
await addressService.AddNewAddressAsync(deliveryAddress);
await userService.UpdateUser(user);
AddNewAddressAsync is a service method that eventually makes the underlying repository create a new address like this:
public async Task<Address> CreateAsync(Address item)
{
await Addresses.InsertAsync(item);
return item;
}
With Addresses being an instance of the IMobileServiceTable interface instantiated like this:
private IMobileServiceTable<Address> Addresses
{
get
{
var table = App.Client.GetTable<Address>();
table.MobileServiceClient.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
return table;
}
}
UpdateUser on the other hand is supposed to patch the existing user by triggering the underlying repository like this:
public async Task<User> PatchAsync(User item)
{
await Users.UpdateAsync(item);
return item;
}
With Users also being an instance of the IMobileServiceTable interface instantiated the very same way as Addresses before:
private IMobileServiceTable<User> Users
{
get
{
var table = App.Client.GetTable<User>();
table.MobileServiceClient.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.Objects;
return table;
}
}
On server side, this is what the controllers do:
AddressController
[Authorize]
public class AddressController : TableController<Address>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var context = new AppContext();
DomainManager = new EntityDomainManager<Address>(context, Request);
}
public async Task<IHttpActionResult> PostAddress(Address address)
{
Address current = await InsertAsync(address);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
...
}
UserController
[Authorize]
public class UserController : TableController<User>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var context = new AppContext();
DomainManager = new EntityDomainManager<User>(context, Request);
}
public Task<User> PatchUser(string id, Delta<User> patch)
{
return UpdateAsync(id, patch);
}
…
}
Here is what is logged when executing the above code:
Insertion of the first address
Request: POST http://localhost:51546/tables/Address
{"$id":"1","id":null,...,"UserId":"8953d3deb9b2459796aa00f43d7416cb","User":null}
iisexpress.exe Information: 0 : Request, Method=POST, Url=http://localhost:51546/tables/Address, Message='http://localhost:51546/tables/Address'
...
iisexpress.exe Information: 0 : Message='Model state is valid. Values: address=SpenceAppService.DataObjects.Address', Operation=HttpActionBinding.ExecuteBindingAsync
...
INSERT [dbo].[Addresses]([Id], [UserId], ...)
VALUES (#0, #1, ...)
SELECT ...
FROM [dbo].[Addresses]
WHERE ##ROWCOUNT > 0 AND [Id] = #0
-- #0: '2a407222f6984052b90d233fa9935286' (Type = String, Size = 128)
-- #1: '8953d3deb9b2459796aa00f43d7416cb' (Type = String, Size = 128)
...
-- Executing asynchronously at 23.05.2019 13:22:10 +02:00
-- Completed in 7 ms with result: SqlDataReader
Committed transaction at 23.05.2019 13:22:11 +02:00
Closed connection at 23.05.2019 13:22:11 +02:00
...
iisexpress.exe Information: 0 : Response, Status=201 (Created), Method=POST, Url=http://localhost:51546/tables/Address, Message='Content-type='application/json; charset=utf-8', content-length=unknown'
...
Response: Created
{"userId":"8953d3deb9b2459796aa00f43d7416cb",...,"id":"2a407222f6984052b90d233fa9935286",...}
Insertion of the second address
Request: POST http://localhost:51546/tables/Address
{"$id":"1","id":null,...,"UserId":"8953d3deb9b2459796aa00f43d7416cb","User":null}
iisexpress.exe Information: 0 : Request, Method=POST, Url=http://localhost:51546/tables/Address, Message='http://localhost:51546/tables/Address'
...
iisexpress.exe Information: 0 : Message='Model state is valid. Values: address=SpenceAppService.DataObjects.Address', Operation=HttpActionBinding.ExecuteBindingAsync
...
INSERT [dbo].[Addresses]([Id], [UserId], ...)
VALUES (#0, #1, ...)
SELECT ...
FROM [dbo].[Addresses]
WHERE ##ROWCOUNT > 0 AND [Id] = #0
-- #0: 'a56e1ca3a7b341a39bc00d22772e39e5' (Type = String, Size = 128)
-- #1: '8953d3deb9b2459796aa00f43d7416cb' (Type = String, Size = 128)
...
-- Executing asynchronously at 23.05.2019 13:22:11 +02:00
-- Completed in 0 ms with result: SqlDataReader
Committed transaction at 23.05.2019 13:22:11 +02:00
Closed connection at 23.05.2019 13:22:11 +02:00
...
iisexpress.exe Information: 0 : Response, Status=201 (Created), Method=POST, Url=http://localhost:51546/tables/Address, Message='Content-type='application/json; charset=utf-8', content-length=unknown'
...
Response: Created
{"userId":"8953d3deb9b2459796aa00f43d7416cb",...,"id":"a56e1ca3a7b341a39bc00d22772e39e5",...}
Patch of the user
Request: PATCH http://localhost:51546/tables/User/8953d3deb9b2459796aa00f43d7416cb
{"$id":"1","id":"8953d3deb9b2459796aa00f43d7416cb",...,"BillingAddress":{"$id":"7","id":"2a407222f6984052b90d233fa9935286",...,"UserId":"8953d3deb9b2459796aa00f43d7416cb","User":null},"DeliveryAddress":{"$id":"8","id":"a56e1ca3a7b341a39bc00d22772e39e5",...,"UserId":"8953d3deb9b2459796aa00f43d7416cb","User":null}}
iisexpress.exe Information: 0 : Request, Method=PATCH, Url=http://localhost:51546/tables/User/8953d3deb9b2459796aa00f43d7416cb, Message='http://localhost:51546/tables/User/8953d3deb9b2459796aa00f43d7416cb'
...
iisexpress.exe Information: 0 : Message='Model state is valid. Values: id=8953d3deb9b2459796aa00f43d7416cb, patch=System.Web.Http.OData.Delta`1[SpenceAppService.DataObjects.User]', Operation=HttpActionBinding.ExecuteBindingAsync
...
Opened connection asynchronously at 23.05.2019 13:22:30 +02:00
SELECT
...
[Limit1].[Id] AS [Id],
...
[Limit1].[BillingAddress_Id] AS [BillingAddress_Id],
[Limit1].[DeliveryAddress_Id] AS [DeliveryAddress_Id]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
...
[Extent1].[BillingAddress_Id] AS [BillingAddress_Id],
[Extent1].[DeliveryAddress_Id] AS [DeliveryAddress_Id]
FROM [dbo].[Users] AS [Extent1]
WHERE [Extent1].[Id] = #p__linq__0
) AS [Limit1]
-- p__linq__0: '8953d3deb9b2459796aa00f43d7416cb' (Type = String, Size = 4000)
...
INSERT [dbo].[Addresses]([Id], [UserId], ...)
VALUES (#0, #1, ...)
SELECT ...
FROM [dbo].[Addresses]
WHERE ##ROWCOUNT > 0 AND [Id] = #0
-- #0: '2a407222f6984052b90d233fa9935286' (Type = String, Size = 128)
-- #1: '8953d3deb9b2459796aa00f43d7416cb' (Type = String, Size = 128)
...
-- Executing asynchronously at 23.05.2019 13:22:30 +02:00
-- Failed in 1 ms with error: Violation of PRIMARY KEY constraint 'PK_dbo.Addresses'. Cannot insert duplicate key in object 'dbo.Addresses'. The duplicate key value is (2a407222f6984052b90d233fa9935286).
The statement has been terminated.
...
The request could not be completed. (Conflict)
Problem
As you can see, while patching the user, the underlying method tries to also insert the addresses that actually have already been inserted, so the error message does perfectly make sense.
However, how do I get this to work?
When I don't insert the addresses first, they don't get ids from the server, so when the userService patches the user which in turn tries to insert the addresses (which then don't exist yet), I get an error message telling me, that the Id field (of the address) is required.
When I add Id fields for both BillingAddress and DeliveryAddress to the user object and skip setting the address fields before updating the user but set their respective ids instead (after inserting the addresses), everything works as expected.
However, I still seem to be missing the üoint, because I thought, Entity Framework should be able to handle all of this internally.
I do know, that working with Azure Appservice, Entity Framework, Code First, an ASP.NET MVC backend and dependent tables accessed by using the Azure Mobile Apps Client and Server API makes things different,... but I was not able to find samples that clearly show how to get this just right...
I also read about having to configure relations either by using fluent API or by decorating properties. However, even when doing nothing, it seems to me as if the framework is able to handle this by itself. For instance when adding the BillingAddressId property to the user data object on server side and executing an Add-Migration, the migration code contains foreign key constraints without me having to explicitly configure them:
public override void Up()
{
AddColumn("dbo.Users", "BillingAddressId", c => c.String(maxLength: 128));
AddColumn("dbo.Users", "DeliveryAddressId", c => c.String(maxLength: 128));
CreateIndex("dbo.Users", "BillingAddressId");
CreateIndex("dbo.Users", "DeliveryAddressId");
AddForeignKey("dbo.Users", "BillingAddressId", "dbo.Addresses", "Id");
AddForeignKey("dbo.Users", "DeliveryAddressId", "dbo.Addresses", "Id");
}
You're User entities aren't aware of the Address objects that were added to the DB. What you want to do is var entity = context.Users.Find(id); and update entity with what's in patch using either entity.Prop = patch.Prop; or using something like Automapper to update the entity and then call your UpdateAsync. This will make the context aware of the new changes made to your User entity and will have the foreign keys populated.
Hope this helps.

Can MSI work with EF CodeFirst?

I've gotten stuck on this for quite a while now, with no luck advancing it on my own.
I am trying to connect from an Azure App Service to a EF CodeFirst managed database, using an MSI token.
When I deployed the App Service using ARM I produced an Output that ensured that it created a Service Principal:
{
"principalId":"98f2c1f2-0a86-4ff1-92db-d43ec0edxxxx","
tenantId":"e6d2d4cc-b762-486e-8894-4f5f440dxxxx",
"type":"SystemAssigned"
}
In Kudu the environment variables show that it is being installed:
MSI_ENDPOINT = http://127.0.0.1:41239/MSI/token/
MSI_SECRET = 7C1B16Fxxxxxxxxxxxxx
I have provided in the Azure Portal a connection string as follows:
Data Source=nzmoebase0000bt.database.windows.net;Initial Catalog=nzmoebase0001bt;Connect Timeout=300;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=300;
I've added the principal to the database as an Owner.
Note: I cannot do the same for the master db.
The token is added to the DbContext as follows:
The token is being added using:
static async Task AttachAccessTokenToDbConnection(IDbConnection dbConnection)
{
SqlConnection sqlConnection = dbConnection as SqlConnection;
if (sqlConnection == null)
{
return;
}
string msiEndpoint = Environment.GetEnvironmentVariable("MSI_ENDPOINT");
if (string.IsNullOrEmpty(msiEndpoint))
{
return;
}
var msiSecret = Environment.GetEnvironmentVariable("MSI_SECRET");
if (string.IsNullOrEmpty(msiSecret))
{
return;
}
// To get around:
// "Cannot set the AccessToken property if 'UserID', 'UID', 'Password', or 'PWD' has been specified in connection string."
var terms = new[] {"UserID","Password","PWD=","UID=" };
string connectionString = dbConnection.ConnectionString;
foreach (var term in terms)
{
if (connectionString.Contains(term, StringComparison.InvariantCultureIgnoreCase))
{
return;
}
}
string accessToken = await AppCoreDbContextMSITokenFactory.GetAzureSqlResourceTokenAsync();
sqlConnection.AccessToken = accessToken;
}
With tracing on, the token is:
.eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI....
Which decoded using jwt.io gave:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "FSimuFrFNoC0sJXGmv13nNZceDc",
"kid": "FSimuFrFNoC0sJXGmv13nNZceDc"
}.{
"aud": "https://database.windows.net/",
"iss": "https://sts.windows.net/e6d2d4cc-b762-486e-8894-4f5f440dxxxx/",
"iat": 1522783025,
"nbf": 1522783025,
"exp": 1522786925,
"aio": "Y2NgYPjNdyJd9zrzpLavJSEzNIuPAAA=",
"appid": "d1057cea-461b-4946-89a9-d76439c2xxxx",
"appidacr": "2",
"e_exp": 262800,
"idp": "https://sts.windows.net/e6d2d4cc-b762-486e-8894-4f5f440dxxxx/",
"oid": "98f2c1f2-0a86-4ff1-92db-d43ec0edxxxx",
"sub": "98f2c1f2-0a86-4ff1-92db-d43ec0edxxxx",
"tid": "e6d2d4cc-b762-486e-8894-4f5f440dxxxx",
"uti": "59bqKWiSL0Gf0bTCI0AAAA",
"ver": "1.0"
}.[Signature]
I Added Persist Security Info = True as per several recommendations on the net, but that did nothing detectable.
Data Source=nzmoebase0000bt.database.windows.net;Initial Catalog=nzmoebase0001bt;MultipleActiveResultSets=False;Persist Security Info = True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;
The error I am getting is:
[InvalidOperationException: This operation requires a connection to the 'master' database. Unable to create a connection to the 'master' database because the original database connection has been opened and credentials have been removed from the connection string. Supply an unopened connection.]
Has anyone gotten a connection to a database, using CodeFirst, with Migrations, and MSI? At this point, after several weeks of being really stuck, I'm starting to wonder if it is possible.
Thanks for any help -- even if just proof that it can work, for starters.
Unfortunately, to my knowledge, no. A major stumbling block to a project that had to fall back to unsecure Username/password loaded connection strings.
You can set the access token on the sql connection like that:
Install the Microsoft.Azure.Services.AppAuthentication nuget package
set up your context class like that:
public class MyDatabaseContext : DbContext
{
public MyDatabaseContext(DbContextOptions<MyDatabaseContext> options)
: base(options)
{
// Get the db connection
var connection = (SqlConnection)Database.GetDbConnection();
// Add the access token
connection.AccessToken = new AzureServiceTokenProvider()
.GetAccessTokenAsync("https://database.windows.net/")
.ConfigureAwait(false).GetAwaiter().GetResult();
}
public DbSet<MyTable> MyTable { get; set; }
}

oracle database gets connected without tns entry

I am new to the oracle database.
I installed OracleXE 11g on my machine. I created a separate test database[SID:testDB] from oracle default database(XE) referring this video. i created below things:
created windows service- OracleServicetestDB using below cmd:
oradim -new -sid testDB -startmode auto -pfile initTestDB.ora
created database
executed sql scripts
SQL> #?\rdbms\admin\catalog.sql
SQL> #?\rdbms\admin\catproc.sql
created user
After creating user/schema for this new database i am able to make connect it from sql developer and java/jdbc programme from other machine on the network.
I am surprised that i have not created any TNS listener or TNS entry for this database in tnsnames.ora but still i am able to connect with this database locally and remotely.
i am expecting answers of below questions:
how my testDB is connected without tns entry?
if testDB is depend on XE service/listener, how i configure OracleServicetestDB to seperate from XE services ?
List item
java/jdbc code:
import java.sql.Connection;
import java.sql.DriverManager;
public class FirstExample {
// JDBC driver name and database URL
static final String JDBC_DRIVER = "oracle.jdbc.driver.OracleDriver";
static final String DB_URL = "jdbc:oracle:thin:#localhost:1521:testDB";
// Database credentials
static final String USER = "testDBUser";
static final String PASS = "password";
public static void main(String[] args) {
Connection conn = null;
try {
// STEP 2: Register JDBC driver
Class.forName(JDBC_DRIVER);
// STEP 3: Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
System.out.println("Connected.");
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
tnsnames.ora is used for the openers (apps/tools) depending on driver used and somtimes connection settings; the Java driver can handle most cases without tnsnames, see http://docs.oracle.com/cd/E11882_01/java.112/e16548/urls.htm#JJDBC08200 and note that "TNSnames alias" is only one of several options.
Listener is configured in ORAHOME/network/admin/listener.ora and usually the defaults don't require any change. On Windows listener runs as a service (a Windows service, not to be confused with an Oracle service-name!) and starts automatically. On both Unix and Windows you have one listener even if you have multiple database instances/SIDs.

Overriding doctrine entity manager at runtime

I'm trying to create a demo system in my Symfony2 project to allow creation of a temporary sqlite demo database for each 'demo' user that logs into a system I'm working on so that each user has access to a default set of data and can modify the data without interfering with other demo users logged into the system.
I've started this by defining a demo connection and an orm entry containing reference to all bundles I want to redirect to the temp db in config.yml then overriding the connection details (more precisely the db path) on login to contain the session id of the user logged in. So in theory, when they login they'll have access to a default set of demo data and when they logout, no changes are persisted and there changes were only visible by them.
$this->doctrine->resetManager('demo');
The problem is, when I try to modify the demo connection to point to a new path, when I call the above step (as mentioned on a few posts I've found scattered around the interwebs) the system dies with an error I'm not familiar with and not sure how to tackle. If somebody could help me understand what's going wrong I should be able to fix it but at the moment I've hit a brick wall :(
Error output: http://i.imgur.com/cllbIyy.png
The process is:
Demo user logs in (in_memory, none database stored)
Login listener overrides 'demo' entity manager, changing the DB location to /tmp/portal_demo_[SESSION_ID].db and populating it with demo data
All following database calls should then use demo database
Here's my code:
Config.yml
doctrine:
dbal:
default_connection: default
connections:
demo:
driver: pdo_sqlite
default:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# Following added to allow importing of existing mysql database. DZH
mapping_types:
enum: string
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
SPSettingsBundle: ~
SPUserBundle: ~
SPCompanyBundle: ~
SPVenueBundle: ~
SPAccessBundle: ~
SPHardwareBundle: ~
SPResellerBundle: ~
SPVouchersBundle: ~
SPTokenBundle: ~
SPAdminBundle: ~
SPSocialBundle: ~
demo:
connection: demo
mappings:
SPUserBundle: ~
SPCompanyBundle: ~
SPVenueBundle: ~
SPAccessBundle: ~
SPHardwareBundle: ~
SPResellerBundle: ~
SPVouchersBundle: ~
SPTokenBundle: ~
SPAdminBundle: ~
SPSocialBundle: ~
SPLegacyPortalBundle:
type: "annotation"
dir: "Entity"
SPLegacyPortalBundle:
type: "annotation"
dir: "Entity/Radius"
Listener
<?php
namespace SP\DemoBundle\Listener;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Doctrine\DBAL\Connection;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Console\Input\ArrayInput;
class DemoLoginListener {
private $defaultConnection;
private $doctrine;
private $kernel;
public function __construct(Connection $defaultConnection, Registry $doctrine, KernelInterface $kernel) {
$this->defaultConnection = $defaultConnection;
$this->doctrine = $doctrine;
$this->kernel = $kernel;
}
/**
* Create initial database schema
*/
public function createDbSchema() {
$application = new \Symfony\Bundle\FrameworkBundle\Console\Application($this->kernel);
$application->setAutoExit(false);
// Run initial schema install
$application->run(new ArrayInput(array(
'doctrine:schema:create',
'--em' => 'demo'
)));
}
/**
* Populate database with demo data
*/
public function populateDemoData() {
$query = <<<EOT
INSERT INTO `Company` (`account_manager_id`, `package_id`, `name`, `owner_first_name`, `owner_last_name`, `telephone`, `mobile`, `email`, `venues`, `created_by`, `date_created`) VALUES
(NULL, NULL, 'Demo Company', 'Demo', 'User', NULL, NULL, 'demo#company.com', 'N;', 1, '2013-05-16 15:06:16');
EOT;
$this->defaultConnection->query($query);
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) {
$token = $event->getAuthenticationToken();
$user = $token->getUser();
// Only create demo database with the 'demo' in_memory user logs in
if ('demo' == $user->getUsername()) {
$request = $event->getRequest();
$session = $request->getSession();
if ($session) {
$db_name = sprintf('/tmp/portal_demo_%s.db', $session->getId());
$this->defaultConnection->close();
$reflectionConn = new \ReflectionObject($this->defaultConnection);
$reflectionParams = $reflectionConn->getProperty('_params');
$reflectionParams->setAccessible('public');
$params = $reflectionParams->getValue($this->defaultConnection);
$params['driver'] = 'pdo_sqlite';
$params['dbname'] = $db_name;
$params['path'] = $db_name;
unset($params['host']);
unset($params['port']);
unset($params['password']);
unset($params['user']);
$reflectionParams->setValue($this->defaultConnection, $params);
$reflectionParams->setAccessible('private');
$this->doctrine->resetManager('demo');
// $this->defaultConnection->connect();
}
}
// Create the naked database
// $this->createDbSchema();
// Populate the database with demo data
// $this->populateDemoData();
}
}

Resources