I'm trying to implement permissions (resources & models ones) on Liferay 7.1. I've noted that BaseModelPermissionChecker is now deprecated (so I should not make my permission classes extending it). So I tried to do as indicated in Liferay official tutorial
However, when deploying my osgi services, OSGI container indicates the following error (for model and resource permission):
ERROR [Refresh Thread: Equinox Container: 80bbc977-c7cb-0018-1000-82f21869d6ec][com_liferay_training_space_gradebook_service:97] [com.a.b.c.service.permission.MyEntityPermission(3887)] Field _portletResourcePermission in component class com.a.b.c.service.permission.MyEntityPermission must not be static.
Have you got that error ? Did I skip something ?
EDIT
Component created:
#Component(immediate = true, service = {})
public class BookResourcePermission {
public static final String ADD_BOOK = "ADD_BOOK";
#Reference(target = "(resource.name=com.x.y.mybook.model)", unbind = "-")
protected static void setPortletResourcePermission(PortletResourcePermission portletResourcePermission) {
_portletResourcePermission = portletResourcePermission;
}
public static boolean contains(PermissionChecker permissionChecker, long groupId, String actionId) {
return _portletResourcePermission.contains(permissionChecker, groupId, actionId);
}
private static PortletResourcePermission _portletResourcePermission;
}
Thanks !
Related
In my Minimal API, I use and integrate with Kofax TotalAgility WCF endpoints. I wanted to implement this integration properly, so I added a remote assembly and added the WCF contract in it along with the service interface and implementation:
Service Interface:
public interface IKofaxService
{
public Task<string> CreateJob(long letterId);
public Task ActionHandler(PortalActionRequest request);
}
Service implementation:
public class KofaxService : IKofaxService
{
private readonly ILogger<KofaxService> logger;
private readonly KofaxSetup config;
private readonly KtaJob.IJobService jobService;
private readonly KtaActivity.IActivityService activityService;
public KofaxService(ILogger<KofaxService> inLogger, KofaxSetup inConfig)
{
logger = inLogger;
// Here is the problem: THe constructor's parameter should be IOptions<Kofaxsetup> instead of just KofaxSetup and this below line will become:
// config = inConfig.Value;
config = inConfig;
//WCF Generated Stuff within this remote assembly
jobService = new KtaJob.JobServiceClient(GetBinding(), GetEndpointAddress(config.KtaUrlApiJob));
activityService = new KtaActivity.ActivityServiceClient(GetBinding(), GetEndpointAddress(config.KtaUrlApiActivity));
}
public async Task<string> CreateJob(long letterId)
{
...
}
public async Task ActionHandler(PortalActionRequest request)
{
...
}
}
In order to have a Servces.AddKofaxTotalAgility() like fluent API, I added the extension method like so (in the remote assembly):
Service extension method:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddKofaxTotalAgility(this IServiceCollection services)
{
services.AddScoped<IKofaxService, KofaxService>();
return services;
}
}
Also in the remote assembly, I have a class representing the setting object from appSetting's section:
Config class:
public class KofaxSetup
{
public string KtaUrlApiActivity { get; set; } = string.Empty;
public string KtaUrlApiJob { get; set; } = string.Empty;
public string SessionId { get; set; } = string.Empty;
public string ProcessId { get; set; } = string.Empty;
}
Back in the Minimal API project, I added a reference to the remote assembly and also have the settings in appSettings.json file:
appSettings.json:
{
...
"KofaxSetup": {
"KtaUrlApiActivity": "https://kofax.somewhere.com/TotalAgility/Services/SDK/ActivityService.svc",
"KtaUrlApiJob": "https://kofax.somewhere.com/TotalAgility/Services/SDK/JobService.svc",
"SessionId": "7DB87F70018D4770BF6114B1C9BA6041",
"ProcessId": "66EC6EED5D024E7AB0013D60F7A04A1A"
},
...
}
Lastly, modifications to Program.cs are as follows:
Minimal API Program.cs
var builder = WebApplication.CreateBuilder(args);
...
// Trigger KofaxSetting object from AppSetting's section
builder.Services.Configure<KofaxSetup>(builder.Configuration.GetSection(nameof(KofaxSetup)));
...
// Add the service to the DI
builder.Services.AddKofaxTotalAgility();
...
All of this just results in this exception at startup:
Exception # var app = builder.Build();
System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: DACRL.Integrations.Kofax.IKofaxService Lifetime: Scoped ImplementationType: DACRL.Integrations.Kofax.KofaxService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.) (Error while validating the service descriptor 'ServiceType: DACRL.Application.Core.Services.ILetterService Lifetime: Transient ImplementationType: DACRL.Api.Services.LetterService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.) (Error while validating the service descriptor 'ServiceType: DACRL.Application.Core.Services.ILetterService Lifetime: Transient ImplementationType: DACRL.Api.Services.LetterService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.)'
1/2:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: DACRL.Integrations.Kofax.IKofaxService Lifetime: Scoped ImplementationType: DACRL.Integrations.Kofax.KofaxService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.
2/2:
InvalidOperationException: Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.
Note that the ILetterService is working properly, and this is the service that internally attempts to receive the IKofaxService from DI in its parameter. I'm thinking the error has something to do with the object KofaxSetup
Is there a best practice that I'm missing here? Am I supposed to have a parameter-less constructor somewhere? Is the Logger<KofaxService> injection within the service's implementation not valid?
I actually sorted the issue out but didn't want to waste a well-written question.
The problem was fact, the KofaxSetup class. I was receiving it as its type directly in the Service's constructor. I had to use IOptions<KofaxSetup> instead to solve the issue.
I want to create a custom FileTarget for NLog (targeting LiteDB). I can't use the existing one since it's missing some features I need and afaik it's not compatible with the latest version of LiteDB. In my custom target I would like to use the TempDir Layout Renderer. Since NLog already has this with the FilePathLayout class, I thought I can reuse it but sadly this is declared as internal.
Am I missing something? Is there any other way to use this?
My setup:
NLog: 4.7.2
NLog.Web.AspNetCore: 4.9.2
ASP.NET Core 3.1
If you are writing a custom NLog target, then you can do this:
[Target("MyFirst")]
public sealed class MyFirstTarget: TargetWithLayout
{
public MyFirstTarget()
{
this.Directory = "${tempdir}";
}
public Layout Directory { get; set; }
protected override void Write(LogEventInfo logEvent)
{
string logMessage = RenderLogEvent(this.Layout, logEvent);
string directory = RenderLogEvent(this.Directory, logEvent);
WriteToDirectory(directory, logMessage);
}
private void WriteToDirectory(string directory, string message)
{
// TODO - write me
}
}
See also: https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target
I'm trying to implement a ModelListener in Liferay 7. When I'm updating a JournalArticle in the UI, I would like to add a small behaviour and some logging.
#Component(immediate = true, service = ModelListener.class)
public class DownloadsListener extends BaseModelListener<JournalArticle> {
#Reference
private StructureService structureService;
#Reference
private DLFileEntryLocalServiceUtil fileEntryLocalService;
private static final Log LOG = LogFactoryUtil.getLog(DownloadsListener.class);
#Override
public void onAfterUpdate(JournalArticle model) throws ModelListenerException {
LOG.error("UPDATES!");
LOG.error("UPDATES!");
super.onAfterUpdate(model);
if(structureService.isNieuwsArticle(model)) {
}
getFileFromURL("/documents/82177/0/file.pdf/0d10338c-8ca1-c5b7-cc4b-011bef1ee759");
}
private DLFileEntry getFileFromURL(String url) {
String[] splittedURL = StringUtil.split(url, '/');
return null;
}
}
But this code is never triggered when I'm updating an article. I'm not hitting my debug point in the onAfterUpdate method, and I'm not seeing any logging. The OSGI Module is deployed correctly. What am I missing?
The issue was that my ModelListener was not being correctly deployed in the OSGi container. (Although Eclipse / Apache Gogo told everything was fine).
OSGi could not resolve:
#Reference
private DLFileEntryLocalServiceUtil fileEntryLocalService;
Changed it to
#Reference
private DLFileEntryLocalService fileEntryLocalService;
Now the behaviour is correctly. It's a shame that there is ZERO feedback by Apache GoGo.
I am in a Jboss AS 7 environment. My application's /admIn/* path is protected by a security-constraint which requires form based authentication. Security domain is database backed.
It's ok but now I want to display a "good morning " in each page's header.
I'm looking for some sort of getLoggedUsername() or getPrincipal() function but I can't find it.
Please post a reference to the official docs if any.
Thank you.
You should be able to use JAAS. Which is what JBoss 7 ought to be using.
The calling principal will be stored in a SessionContext which you can obtain by telling JBoss it's a resource.
#Resource
private SessionContext context;
public void myAwesomeMethod() {
String currentUser = context.getCallerPrincipal().getName();
}
If for some reason the Injection doesn't work on a Stateless bean, you can look up the EJBContext direct.
#Stateless
public class HelloBean implements com.foo.ejb.HelloRemote {
public void hello() {
try {
InitialContext ic = new InitialContext();
SessionContext sctxLookup =
(SessionContext) ic.lookup("java:comp/EJBContext");
System.out.println("look up EJBContext by standard name: " + sctxLookup);
} catch (NamingException ex) {
throw new IllegalStateException(ex);
}
}
}
This snippet was obtained from 4 ways to obtain EJBContext.
I wrote about this topic in another question.
However, I've since refactored my code to get rid of configuration access, thus allowing the specs to pass. Or so I thought. They run fine from within Visual Studio using TestDriven.Net. However, when I run them during rake using the mspec.exe tool, they still fail with a serialization exception. So I've created a completely self-contained example that does basically nothing except setup fake security credentials on the thread. This test passes just fine in TD.Net, but blows up in mspec.exe. Does anybody have any suggestions?
Update: I've discovered a work-around. After researching the issue, it seems the cause is that the assembly containing my principal object is not in the same folder as the mspec.exe. When mspec creates a new AppDomain to run my specs, that new AppDomain has to load the assembly with the principal object in order to deserialize it. That assembly is not in the same folder as the mspec EXE, so it fails. If I copied my assembly into the same folder as mspec, it works fine.
What I still don't understand is why ReSharper and TD.Net can run the test just fine? Do they not use mspec.exe to actually run the tests?
using System;
using System.Security.Principal;
using System.Threading;
using Machine.Specifications;
namespace MSpecTest
{
[Subject(typeof(MyViewModel))]
public class When_security_credentials_are_faked
{
static MyViewModel SUT;
Establish context = SetupFakeSecurityCredentials;
Because of = () =>
SUT = new MyViewModel();
It should_be_initialized = () =>
SUT.Initialized.ShouldBeTrue();
static void SetupFakeSecurityCredentials()
{
Thread.CurrentPrincipal = CreatePrincipal(CreateIdentity());
}
static MyIdentity CreateIdentity()
{
return new MyIdentity(Environment.UserName, "None", true);
}
static MyPrincipal CreatePrincipal(MyIdentity identity)
{
return new MyPrincipal(identity);
}
}
public class MyViewModel
{
public MyViewModel()
{
Initialized = true;
}
public bool Initialized { get; set; }
}
[Serializable]
public class MyPrincipal : IPrincipal
{
private readonly MyIdentity _identity;
public MyPrincipal(MyIdentity identity)
{
_identity = identity;
}
public bool IsInRole(string role)
{
return true;
}
public IIdentity Identity
{
get { return _identity; }
}
}
[Serializable]
public class MyIdentity : IIdentity
{
private readonly string _name;
private readonly string _authenticationType;
private readonly bool _isAuthenticated;
public MyIdentity(string name, string authenticationType, bool isAuthenticated)
{
_name = name;
_isAuthenticated = isAuthenticated;
_authenticationType = authenticationType;
}
public string Name
{
get { return _name; }
}
public string AuthenticationType
{
get { return _authenticationType; }
}
public bool IsAuthenticated
{
get { return _isAuthenticated; }
}
}
}
Dan,
thank you for providing a reproduction.
First off, the console runner works differently than the TestDriven.NET and ReSharper runners. Basically, the console runner has to perform a lot more setup work in that it creates a new AppDomain (plus configuration) for every assembly that is run. This is required to load the .dll.config file for your spec assembly.
Per spec assembly, two AppDomains are created:
The first AppDomain (Console) is created
implicitly when mspec.exe is
executed,
a second AppDomain is created by mspec.exe for the assembly containing the specs (Spec).
Both AppDomains communicate with each other through .NET Remoting: For example, when a spec is executed in the Spec AppDomain, it notifies the Console AppDomain of that fact. When Console receives the notification it acts accordingly by writing the spec information to the console.
This communiciation between Spec and Console is realized transparently through .NET Remoting. One property of .NET Remoting is that some properties of the calling AppDomain (Spec) are automatically included when sending notifications to the target AppDomain (Console). Thread.CurrentPrincipal is such a property. You can read more about that here: http://sontek.vox.com/library/post/re-iprincipal-iidentity-ihttpmodule-serializable.html
The context you provide will run in the Spec AppDomain. You set Thread.CurrentPrincipal in the Because. After Because ran, a notification will be issued to the Console AppDomain. The notification will include your custom MyPrincipal that the receiving Console AppDomain tries to deserialize. It cannot do that since it doesn't know about your spec assembly (as it is not included in its private bin path).
This is why you had to put your spec assembly in the same folder as mspec.exe.
There are two possible workarounds:
Derive MyPrincipal and MyIdentity from MarshalByRefObject so that they can take part in cross-AppDomain communication through a proxy (instead of being serialized)
Set Thread.CurrentPrincipal transiently in the Because
(Text is required for formatting to work -- please ignore)
Because of = () =>
{
var previousPrincipal = Thread.CurrentPrincipal;
try
{
Thread.CurrentPrincipal = new MyPrincipal(...);
SUT = new MyViewModel();
}
finally
{
Thread.CurrentPrincipal = previousPrincipal;
}
}
ReSharper, for example, handles all the communication work for us. MSpec's ReSharper Runner can hook into the existing infrastructure (that, AFAIK, does not use .NET Remoting).