Make mandatory one of two options - spring-shell

I have a command like this
#CliCommand("show user")
public String showUser(
#CliOption(key = {"email"}) String email,
#CliOption(key = {"id"}) long id) {
//return user by id or by email
}
I want to make one of the two option mandatory.
show user --id 5 //valid
show user --email user#email.com //valid
show user //not valid
show user id 5 --email user#email.com //not valid
How can I achieve this behavior?

You'll need to handle the validation inside the command implementation itself (and throw some exception if both or none of your options are set).
The id option parameter should be set to type Long instead of long then.

Related

Pass multiple optional parameters to Controller Action

I have a controller Action that returns a list of activities to a view. (eventID,Venue,Room,EventDescription,EventType,StartDateTime,EndDateTime). The users wanted to be able to filter by Venue so I added Venue as id to the action method
ActionResult ListEvents(id string)
{
... Get the relevant details and return the view with the model
}
Now they want to also be able to filter by any/all of Event Type, Start, End, whether Post-event data has been completed.
Am I better to add these as GET query parameters or to define a custom route that will accept all 5 arguments or is there another solution
I will also need to add sorting and pagination at some point in case this changes the suggestion.
Typically, these would be handled via a query string, but it doesn't matter how you do it really. Regardless, of how the parameters are sent, your action simply needs to accept them all. The only thing you have to be aware of is the standard C# method rule (since actions are just methods) that optional parameters must be the last ones on the method. If they're all optional, then even that isn't really a concern.
Basically, you just have something like:
public ActionResult ListEvents(string id = null, int? eventID = null, ...)
{
Then inside, you'd just do something like:
var events = db.Events;
if (eventID.HasValue)
{
events = events.Where(m => m.EventID == eventId);
}
// rinse and repeat for each filter

Authentication in liferay pages

We are having a portlet on a liferay page. We want to put up up a permission on every action method that is performed. For example on page A we have landed an XYZ portlet. Now we want that whenever there is any action performed form this portlet, we want to check that if the user is having a role to perform this action or not.
It wont be a good approach to put up the code in Action method of the portlet cause we are having approximately 20 such pages and portlets.
Can we have some sort of filter or so, so that each action request is checked if the user is having the access to the content or not.
Thank you...
My idea.
Use a filter to intercept all request
You can add a filter to the Liferay Servlet to check every request.
For that you can use a hook-plugin.
Look at this :
http://www.liferay.com/fr/documentation/liferay-portal/6.1/development/-/ai/other-hooks
http://connect-sam.com/2012/06/creating-servlet-filter-hook-in-liferay-6-1-to-restrict-access-based-on-ip-location/
Issue with filter is that you can't access ThemeDisplay or use PortalUtil.getUser(request).
So you must use work around like that :
private User _getUser(HttpServletRequest request) throws Exception {
HttpSession session = request.getSession();
User user = PortalUtil.getUser(request);
if (user != null) {
return user;
}
String userIdString = (String) session.getAttribute("j_username");
String password = (String) session.getAttribute("j_password");
if ((userIdString != null) && (password != null)) {
long userId = GetterUtil.getLong(userIdString);
user = UserLocalServiceUtil.getUser(userId);
}
return user;
}
Filtering the request
To filter the request you must get :
page id (Layout id in Liferay)
portlet id
portlet lifecycle
One more time using a filter is a pain because you can get the ThemeDisplay. These params are easy to get (with real object instancee) with ThemeDisplay.
So you must get this as parameter in the request.
final String portletId = ParamUtil.get((HttpServletRequest) servletRequest, "p_p_id", "");
final String layoutId = ParamUtil.get((HttpServletRequest) servletRequest, "plid", "");
final String portletLifecycle = ParamUtil.get((HttpServletRequest) servletRequest, "p_p_lifecycle", "");
Lifecycle details :
portletLifecycle is a int and the meaning of value is :
0 : RENDER
1 : ACTION (the one that interests you)
2 : RESOURCE
I think that with this data you can be able to define if user can or cannot make the action.
You can get user roles from the user.
You can get the current page and portlet linked to the request.
And you can know if the request is an action request.
Good luck with Liferay.
You can add freely configurable permissions to Liferay, see the Developer Guide for detailed information. My first guess on this would be that these affect "model resources", e.g. the data that your portlet is dealing with, rather than portlet-resources, e.g. permissions on the individual portlet itself. Think of portlet-permissions as permissions that are defined by Liferay, model-resources as permissions where you can come up with your own vocabulary on the actions, e.g. "UPDATE_ADDRESS" etc.
These permissions will typically be tied to roles, which are granted to users/usergroups/etc.
Based on this variability, it depends on the nature of your permissions if you can write a filter to generically check permissions, or if it depends on more than the individual action call.
If you determine that there is a generic solution, look up PortletFilters, they behave just like ServletFilters. These can easily provide a home for permission checks.
It's quite hard to cover this topic in such a short answer, I hope to have given enough resources for you to continue your quest.
You can abuse some existing portlet permission like "Add to Page" and set it to roles that should call the action.
And by the rendering and action phases validate "has the user necessary permission".
Or you can create new permission and configure it by portlet-configuration. This way is cleaner, but difficulty.

Edit Custom Attribute after Log In

I can only Edit a custom field when I before edit it by hand with the user administrator.
What's wrong with my code and what I should do to solve this??
Exactly, I'm trying to assign a value to a User custom attribute when It logs in the portal. And I'm not able to get ExpandoColumn in the conditions specified.
The problem is that ExpandoValue is null.
public class LoginAction extends Action {
public void run(HttpServletRequest req, HttpServletResponse res) {
User currentUser;
try {
currentUser = PortalUtil.getUser(req);
long userId = PrincipalThreadLocal.getUserId();
long companyId = currentUser.getCompanyId();
long groupId = GroupLocalServiceUtil.getGroup(companyId, "Guest").getGroupId();
/* Get de CustomField Segmentation */
ExpandoTable expandoTable = ExpandoTableLocalServiceUtil.getDefaultTable(companyId, User.class.getName());
ExpandoColumn expandoColumn = ExpandoColumnLocalServiceUtil.getColumn(companyId, User.class.getName(), expandoTable.getName(), "Segmentation");
if (expandoColumn != null) {
ExpandoValue expandoValue = ExpandoValueLocalServiceUtil.getValue(expandoTable.getTableId(), expandoColumn.getColumnId(), userId);
if (expandoValue != null) {
expandoValue.setData(finalsegment);
ExpandoValueLocalServiceUtil.updateExpandoValue(expandoValue);
}
}
}
Summary: My problem is that
ExpandoValue expandoValue = ExpandoValueLocalServiceUtil.getValue(expandoTable.getTableId(), expandoColumn.getColumnId(), classPK);
is Null when I access the Value of Custom Attribute. If I edit by hand this customattribute and then execute the same Code it works!!! I don't know why and I dont know how to solve this.
Edit (after your update to the question)
Take a look at the ExpandoValueLocalService javadoc: You'll find that there's a createExpandoValue method. Now guess the relationship between the scenario "You have not manually set the value at all, and you're getting back null as ExpandoValue" vs. "You have set it once and get back a value that you can update...
Another option would also be to just specify a default value for your expando value - this way you'll definitely have a value in there and you can unconditionally update it (at least until it's deliberately deleted - for robustness you should still cater for this possibility)
Original answer:
Where else but in the if condition do you go? Have you tried an else condition or do you get any exception before? E.g. you might need to create the 'default' table before you can just get it.
See this code for an example on how to access Expando Tables/Columns.
I didn't run your code, but of course exceptions might occur earlier as well. Or you might have made a mistake in configuring your LoginAction, so that it doesn't run at all.
By default, regular User role has no permissions to access Expando values.
Anyway, it is better to modify expando values with
User user = UserLocalService.getUserById(userId);
user.getExpandoBridge().setAttribute("attributeName", "attributeValue");
If you want to modify value with any permissions, use
user.getExpandoBridge().setAttribute("attributeName", "attributeValue", false);
Here I found the answer of the question..
You can manage the solution with the code proposed.
http://www.liferay.com/es/community/forums/-/message_boards/message/26154530

hard luck with RBAC in yii

i am trying to learn RBAC following various online tutorials and docs on yii and finally ends with something like below i am missing something but i dont know what. I studied the theory and tutorials twice but still getting hard with practical implementation so finally i decided to ask the SO community for the help. what i did till now is exactly below
**I step**
create a table with fields: username,password,email,role
role is enum datatype with 4 roles values ('superadmin','admin','useractive','userpassive')
**II step**
then i imported the schema-mysql.sql file in my database from the framework/web/auth folder of my yii setup.
**III step**
configured my config.php for CDbauthmanager
'authManager'=>array(
'class'=>'CDbAuthManager',
'connectionID'=>'db',
'itemTable'=>'AuthItem',
'itemChildTable'=>'AuthItemChild',
'assignmentTable'=>'AuthAssignment',
),
**IV step**
then i added few lines to UserIdentity.php
public function authenticate()
{
$user = Users::model()->findByAttributes(array('email'=>$this->username));
if ($user===null) { // No user found!
$this->errorCode=self::ERROR_USERNAME_INVALID;
}
else if ($user->password !== $this->password )
{ // Invalid password!
$this->errorCode=self::ERROR_PASSWORD_INVALID;
} else { // Okay!
$this->errorCode=self::ERROR_NONE;
// Store the role in a session:
$this->setState('roles', $user->role);
$this->_id = $user->id;
}
return !$this->errorCode;
}
public function getId()
{
return $this->_id;
}
**V step**
then i inserted values manually in the RBAC required table i.e. AuthItem,AuthItemChild,AuthAssignment
AuthItem table values
================================================================
name type description bizrule data
user1 2 the user1 role NULL NULL
updateProfile 0 update profile NULL NULL
================================================================
AuthItemChild
================================================================
parent child
user1 updateProfile
================================================================
AuthAssignment table values
================================================================
itemname userid bizrule data
user1 1 NULL NULL
And My users table
=================================================================
username password email role
test1 pass1 tes1#local.com user1
**VI step**
after that i tried to play with a controller
public function actionIndex()
{
if(Yii::app()->user->checkAccess('updateProfile'))
{
echo "yes";
}
else
{
echo "missing something";
}
}
now when i logging and tries to access the controller with user1 it show "missing something" but i have assign the user same role. What the Hell I am missing.
This is what i did exactly what part i m missing i dont know i hardly able to do this much.
Thanks all for your precious time
Be careful with the role you called "user1". In case you didn't know yet: This is not your user but simply a role that is called user1, might be confusing if you come back to it in the future :)
The data in your tables looks okay. You added a role (user1), an operation (updateProfile) and you linked the updateProfile action as a child of "user1". Also your AuthAssignment indicates that the role user1 is added to userid 1 so that is all well.
I think the issue is with the fact that you never seem to do anything with the CWebUser instance. Yii::app()->user is a CWebUser instance and it needs to have a user id associated with it. Since I see no code assigning this, it's probably NULL. NULL means that Yii considers the user a guest. You should try to do a var_dump of Yii::app()->user->id to make sure;
The correct way is to have a line somewhere that does the login, like so:
Yii::app()->user->login(, );
Either that or take the lazy way out and do a Yii::app()->user->id = ;

"Domain Users" group is empty when I use DirectoryServices "member" property

I'm using the following code to get the members of a group on my domain:
Dim de As New DirectoryEntry("LDAP://" & GroupDN)
For Each user As String In CType(de.Properties("member"), IEnumerable)
GroupCollection.Add(Username, Username)
Next
My problem is that when GroupDN (the distinguishedname of the group) is "CN=Domain Users,CN=Users,DC=Mydomain,DC=local", the For...Each loop doesn't execute, and when I check the Properties statement manually, it's got a count of zero. This seems to work for every other group in my domain, but the "Domain Users" group should contain everybody, and it appears to contain nobody.
I've checked, and the group lists everybody correctly in my Windows AD tools. Is there something obvious that I'm missing here? On a side note, is there a better way to get all the members of a group?
Unless you change the primary group id of a user, the user is not stored in the member attribute of the Domain Users group, rather it uses the fact that the primary group id is set to the Domain Users RID to determine membership in Domain Users. The normal case is that the Domain Users member attribute is empty; it would require that you make some changes to the default Active Directory implementation for this to not be the case.
The Domain Users group uses a
"computed" mechanism based on the
"primary group ID" of the user to
determine membership and does not
typically store members as
multi-valued linked attributes. If the
primary group of the user is changed,
their membership in the Domain Users
group is written to the linked
attribute for the group and is no
longer calculated. This was true for
Windows 2000 and has not changed for
Windows Server 2003.
Reference
The accepted answer is absolutely correct. By default every (user) object has 513 set in the property primarygroupid, which is the fixed "tail" of the Domain Users sid. But: this can be changed and every other group can be configured there, so we can't rely on that.
Here is an example method how to get the group members anyway (regardless if the default is kept or changed). I call a method like this after any query for members of active directory groups. In this example I get an array of distinguished names as result. But all other properties are possible, just add them to dSearcher.PropertiesToLoad.Add(...) and modify the result.
I know, this is a question about VB, I hope its easy to port it.
using System.DirectoryServices;
using System.Security.Principal;
public static string[] GetMembersDnByPrimaryGroupId(string domainName, SecurityIdentifier sidOfGroupToGetMembersByPrimaryGroupId)
{
// In a single domain environement the domain name is probably not needed, but
// we expect a multy domain environement
if (string.IsNullOrWhiteSpace(domainName) || sidOfGroupToGetMembersByPrimaryGroupId == null)
{
throw new ArgumentNullException($"Neither domainName nor sid may be null / blank: DomainName: { domainName }; sid: { sidOfGroupToGetMembersByPrimaryGroupId }");
//<----------
}
List<string> membersDnResult = new List<string>();
// Get the last segment of the group sid, this is what is stored in the "primaryGroupId"
string groupSidTail = sidOfGroupToGetMembersByPrimaryGroupId.Value.Split('-').Last();
string path = $"LDAP://{ domainName }";
DirectoryEntry dEntry = new DirectoryEntry(path);
SearchResultCollection adSearchResult = null;
DirectorySearcher dSearcher = new DirectorySearcher(dEntry);
// For this example we need just the distinguished name but you can add
// here the property / properties you want
dSearcher.PropertiesToLoad.Add("distinguishedName");
// set the filter to primarygroupid
dSearcher.Filter = $"(&(primarygroupid={ groupSidTail }))";
// May die thousand deaths, therefore exception handling is needed.
// My exception handling is outside of this method, you may want
// to add it here
adSearchResult = dSearcher.FindAll();
// Get the domains sid and check if the domain part of the wanted sid
// fits the domain sid (necesarry in multy domain environments)
byte[] domainSidBytes = (byte[])dEntry.Properties["objectSid"].Value;
SecurityIdentifier domainSid = new SecurityIdentifier(domainSidBytes, 0);
if (sidOfGroupToGetMembersByPrimaryGroupId.AccountDomainSid != domainSid)
{
throw new ArgumentException($"Domain sid of the wanted group { sidOfGroupToGetMembersByPrimaryGroupId.AccountDomainSid } does not fit the sid { domainSid } of the searched through domain \"{ domainName }\"");
//<----------
}
// We found entries by the primarygroupid
if (adSearchResult.Count > 0)
{
foreach (SearchResult forMemberByPrimaryGroupId in adSearchResult)
{
// Every AD object has a distinguishedName, therefore we acess "[0]"
// wihtout any further checking
string dn = forMemberByPrimaryGroupId.Properties["distinguishedName"][0].ToString();
membersDnResult.Add(dn);
}
}
return membersDnResult.ToArray();
}