Methods of running with Elevated Privileges in a MOSS Publishing Site - sharepoint

I am wondering why the two methods listed below do not give the same security trimming.
Expected result: Both methods give full access to all content in the current site collection
Actual result: Security trimming is occurring when using Method #1
Method #2 works properly for retrieving content from other webs, but Method #1 does not.
Both methods give access across webs in Anonymous mode, and both work for site admin accounts.
The difference comes for Hierarchy Managers, Approvers and Editors. Method #1 does not give admin access across webs.
Method #1
using (SystemOperation op = new SystemOperation())
{
//Do an operation that requires retrieving across webs
}
public class SystemOperation : IDisposable
{
private WindowsImpersonationContext ctx;
public SystemOperation()
{
if (!WindowsIdentity.GetCurrent().IsSystem)
{
ctx = WindowsIdentity.Impersonate(System.IntPtr.Zero);
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool all)
{
if (ctx != null)
{
ctx.Undo();
}
}
}
Method #2:
Microsoft.Sharepoint.SPSecurity.RunWithElevatedPrivileges(delegate()
{
//Do an operation that requires retrieving across webs
});

RunWithElevatedPrivileges provides two separate privledges. First is that it elevates the Windows identity of the user to the AppPool account, the second is that it also elevates the identity to the SharePoint\System account which is a built in security account that provides full control (in a SharePoint sense). The internal SharePoint account is used when you construct your SP Objects (like SPSite).
So basically it will depend on how you build your code and when you instatiate your objects that affect how the privledges work out.

Related

Automatic binding to some service properties in Catel with Fody

Assuming there is some service:
public interface IDeviceManagerService
{
ISomeDeviceApi Api { get; }
}
It's purpose is to monitor external environment (USB, network, etc.), instantiate device API when the device detected and make property null when the device is no longer available.
Supposing there is a view model with this service injected, I would like to have change notifications for IDeviceManagerService.Api to make things like below possible (for example, having the button which is only active when the device API is available).
private Boolean OnSomeCommandCanExecute()
{
return _deviceManagerService.Api != null;
}
I wonder if there is a clean way to make this work without manual change notifications handling (with Catel.Fody or PropertyChanged.Fody). So far I have managed to get working result by making service implementation derived from ModelBase, registering it's injected instance as a [Model] inside the view model and exposing it's Api property using [ViewModelToModel] attribute, but this is very dirty way.
Is there some common approach or It would be better to go with implementing INotifyPropertyChanged and use notifications wrapper instead?
In most approaches, services don't implement INotifyPropertyChanged (they are not models), so my recommendation is to add manual events:
public interface IDeviceManagerService
{
ISomeDeviceApi Api { get; }
event EventHandler<DeviceApiEventArgs> ApiChanged;
}
This way you can deal with the stuff you are interested in (subscribe in InitializeAsync, unsubscribe in CloseAsync).

Servicestack Multitenancy dynamic plugins

We are moving from an on premise-like application to a multi tenant cloud application.
for my web application we made a very simple interface based on IPlugin, to create a plugin architecture. (customers can have/install different plugins)
public interface IWebPlugin : IPlugin
{
string ContentBaseUrl { set; get; }
}
We have some plugins that would normally be loaded in on startup. Now i'm migrating the code to load at the beginning of a request (the Register function is called on request start), and scope everything inside this request.
It's not ideal but it would bring the least impact on the plugin system for now.
I could scope the Container by making an AppHost child container which would stick to the request:
Container IHasContainer.Container
{
get
{
if (HasStarted)
return ChildContainer;
return base.Container;
}
}
public Container ChildContainer
{
get { return HttpContext.Current.Items.GetOrAdd<Container>("ChildContainer", c => Container.CreateChildContainer()); }
}
problem case
Now im trying to make plugins work that actually add API services.
appHost.Routes.Add<GetTranslations>("/Localizations/translations", ApplyTo.Get);
But this service is unreachable (and not visible in metadata). How do i make it reachable?
I see you execute the following in ServiceController AfterInit. Re-executing this still wouldnt make it work.
//Copied from servicestack repo
public void AfterInit()
{
//Register any routes configured on Metadata.Routes
foreach (var restPath in appHost.RestPaths)
{
RegisterRestPath(restPath);
//Auto add Route Attributes so they're available in T.ToUrl() extension methods
restPath.RequestType
.AddAttributes(new RouteAttribute(restPath.Path, restPath.AllowedVerbs)
{
Priority = restPath.Priority,
Summary = restPath.Summary,
Notes = restPath.Notes,
});
}
//Sync the RestPaths collections
appHost.RestPaths.Clear();
appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
appHost.Metadata.AfterInit();
}
solution directions
Is there a way i could override the route finding? like extending RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
Or could i restart the path compilation/caching? (would be enough for now that the service would be reachable tenant wide )
All configuration in ServiceStack should be contained within AppHost.Configure() and remain immutable thereafter. It's not ThreadSafe to modify ServiceStack's Static Configuration at runtime like trying to modify registered routes or Service Metadata which needs to be registered once at StartUp in AppHost.Configure().
It looks as though you'll need to re-architect your solution so all Routes are registered on Startup. If it helps Plugins can implement IPreInitPlugin and IPostInitPlugin interfaces to execute custom logic before and after Plugins are registered. They can also register a appHost.AfterInitCallbacks to register custom logic after ServiceStack's AppHost has been initialized.
Not sure if it's applicable but at runtime you can "hi-jack Requests" in ServiceStack by registering a RawHttpHandler or a PreRequestFilter, e.g:
appHost.RawHttpHandlers.Add(httpReq =>
MyShouldHandleThisRoute(httpReq.PathInfo)
? new CustomActionHandler((req, res) => {
//Handle Route
});
: null);
Simple answer seems to be, no. The framework wasn't build to be a run-time plugable system.
You will have to make this architecture yourself on top of ServiceStack.
Routing solution
To make it route to these run-time loaded services/routes it is needed to make your own implementation.
The ServiceStack.HttpHandlerFactory checks if a route exist (one that is registered on init). so here is where you will have to start extending. The method GetHandlerForPathInfo checks if it can find the (service)route and otherwise return a NotFoundHandler or StaticFileHandler.
My solution consists of the following code:
string contentType;
var restPath = RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
//Added part
if (restPath == null)
restPath = AppHost.Instance.FindPluginServiceForRoute(httpMethod, pathInfo);
//End added part
if (restPath != null)
return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
technically speaking IAppHost.IServiceRoutes should be the one doing the routing. Probably in the future this will be extensible.
Resolving services
The second problem is resolving the services. After the route has been found and the right Message/Dto Type has been resolved. The IAppHost.ServiceController will attempt to find the right service and make it execute the message.
This class also has init functions which are called on startup to reflect all the services in servicestack. I didn't found a work around yet, but ill by working on it to make it possible in ServiceStack coming weeks.
Current version on nuget its not possible to make it work. I added some extensibility in servicestack to make it +- possible.
Ioc Solution out of the box
For ioc ServiceStack.Funq gives us a solution. Funq allows making child containers where you can register your ioc on. On resolve a child container will, if it can't resolve the interface, ask its parent to resolve it.
Container.CreateChildContainer()

Enable Orchard module on all tenants without having to do so manually.

Currently running a site with 100+ tenants and have to manually enable all the modules. Is there a way to enable all modules in orchard across all my tenants without having to go to each site and click enable or use manually use the command prompt. I am also having the same issue deploying contents to multiple site. my set up is unique to where i have different database for each tenants.
Below is an example of how I enable/disable features from my modules' migration classes. What you could do is create a command line implementation (inherit from DefaultOrchardCommandHandler) that will fan out your enable/disable module commands to all the tenants.
I'm not sure how the tenant system works in terms fo command line, i think you may have to launch a separate command line bin\orchard.exe session for each tenant. If that's the case you can do the fanning in a script that calls into bin\orchard.exe. If you can do tenant specific commands for different tenants from a single session of bin\orchard.exe, you can do the fanning inside your Commands class.
[UsedImplicitly]
public class Migrations : DataMigrationImpl {
public int UpdateFrom2() {
var features = _moduleService.GetAvailableFeatures().ToDictionary(m=>m.Descriptor.Id, m=>m);
DisableFeature(features, "TinyMce");
EnableFeature(features, "TinyMceDeluxe");
EnableFeature(features, "Contrib.Cache");
EnableFeature(features, "WebAdvanced.Sitemap");
return 3;
}
private void DisableFeature(Dictionary<string, Orchard.Modules.Models.ModuleFeature> features, string featureId) {
if (features.ContainsKey(featureId) && features[featureId].IsEnabled) {
_moduleService.DisableFeatures(new string[] { featureId });
}
}
private void EnableFeature(Dictionary<string, Orchard.Modules.Models.ModuleFeature> features, string featureId) {
if (features.ContainsKey(featureId) && !features[featureId].IsEnabled) {
_moduleService.EnableFeatures(new string[] { featureId });
}
}
}

NServiceBus Unit of Work For Multitenancy with Custom ORM

Here are my parameters:
Simple NServiceBus Saga implementation using the default builder
In-house ORM on top of SQL Server
Multitenancy - I have two ASP.NET MVC 4 domains running on the same website, each with their own databases
We configure our ORM using a static method like so:
public class EndpointConfig: IConfigureThisEndpoint, IWantCustomInitialization {
public void Init() {
var bus = Configure.With()
.AutofacBuilder()
.UnicastBus().LoadMessageHandlers().DoNotAutoSubscribe()
.XmlSerializer()
.MsmqTransport().IsTransactional(true).PurgeOnStartup(false)
.MsmqSubscriptionStorage()
.Sagas().RavenSagaPersister().InstallRavenIfNeeded()
.UseInMemoryTimeoutPersister()
.CreateBus()
.Start();
SlenderConfiguration.Init(bus);
}
}
public class SlenderCofnigruation {
private static ORMScope scope { get; set; }
public static void Init(IBus bus)
{
ORMConfig.GetScope = () =>
{
var environment = "dev";
if (bus.CurrentMessageContext.Headers.ContainsKey("Environment"))
environment = bus.CurrentMessageContext.Headers["Environment"];
if (scope == null)
scope = new SlenderScope(ConfigurationManager.ConnectionStrings[environment].ConnectionString);
return scope;
};
}
}
This works fine in our single-tenant Beta environment - it's fine for that static scope to get re-used because the environment header is always the same for a given deployment.
It's my understanding that this won't work for the multitenant situation described above, because NServiceBus will reuse threads across messages. The same scope would then be used, causing problems if the message was intended for a different environment.
What I think I want is a single scope per message, but I'm really not sure how to get there.
I've seen Unit Of Work Implementation for RavenDB, and the unit of work implementation in the full duplex sample, but I'm not sure that's the right path.
I've also seen the DependencyLifecycle enum, but I'm not sure how I can use that to resolve the scope given the way I have to set up the GetScope func.
Obviously I have no idea what's going on here. Any suggestions?
If you need to do something on a per-message basis, consider using message mutators (IMutateIncomingMessages) in addition to your unit-of-work management with some thread-static state.

Plugin bypassed when Entity queried from console application

My plugin encrypts/decrypts a field. Works on the field within a CRM form.
From my console application, a retrieve bypasses my plugin, e.g., it retrieves the encrypted value directly from the database without running the plugin. When debugging, breakpoints in the plugin are hit when the field is accessed from a form , but they are not hit when accessed from my console program.
I'm surprised that my plugin isn't invoked from a program. It bypasses my business rules.
Here is how I'm accessing the entity and the field from a program:
private static OrganizationServiceProxy service = null;
private static OrganizationServiceContext orgSvcContext = null;
public static void RetrieveSSNs()
{
var query = orgSvcContext.CreateQuery("bpa_consumer");
foreach (Entity consumer in query)
{
if (consumer.Attributes.Contains("bpa_ssn"))
{
string ssn = consumer["bpa_ssn"].ToString();
Console.WriteLine(string.Format("Consumer \"{0}\" has SSN {1}", consumer.Attributes["bpa_name"], ssn));
}
else
{
Console.WriteLine(string.Format("Consumer \"{0}\" doesn't have a SSN", consumer.Attributes["bpa_name"]));
}
}
}
I'm guessing you have the plugin registered on the Retrieve method? If so, add another identical registration on the RetrieveMultiple. This should get your plugin to execute on your foreach. I should warn you that this is an extremely dangerous thing to do from a performance standpoint though...
If you are concerned about performance my recommendation is to put the encrypted data into a separate entity with a lookup back. Using this method CRM only has to execute the Retrieve/RetrieveMultiple plug-in when a user needs to access the encrypted data, not every time a user accesses the primary record. This will also make it easier to secure the encrypted data.
Turns out the you must register your plugin for the event RetrieveMultiple when you query for a collection of Entities.

Resources