/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.lang.op;
import divconq.hub.DomainInfo;
import divconq.hub.Hub;
import divconq.struct.ListStruct;
import divconq.struct.RecordStruct;
/**
* User Context works in conjunction with Task Context and Session to track information about
* Authenticated User or Guest User.
*
* @author Andy
*
*/
public class UserContext {
static public UserContext allocate(OperationContextBuilder tcb) {
return new UserContext(tcb.values);
}
/**
* @return create a new guest context
*/
static public UserContext allocateGuest() {
OperationContext currcon = OperationContext.getOrHub();
// keep the domain id if we can
return new OperationContextBuilder()
.withGuestUserTemplate()
.withDomainId(currcon.getUserContext().getDomainId())
.toUserContext();
}
/**
* @return create a new root context
*/
static public UserContext allocateRoot() {
return new OperationContextBuilder().withRootUserTemplate().toUserContext();
}
/**
* @param m create a task context from a message (RPC calls to dcBus), keep in mind
* this is info gathering only, message must not be allowed to force an
* authenticated/elevated state inappropriately - from RPC clear "Elevated"
* field before calling this
* @return user context from the record
*/
static public UserContext allocate(RecordStruct m) {
return new UserContext(m);
}
static public UserContext allocateFromTask(RecordStruct m) {
return new UserContext(m.deepCopyExclude("OpId", "SessionId", "Origin", "DebugLevel", "Elevated", "Gateway", "OpLocale"));
}
static public OperationContextBuilder checkCredentials(UserContext ctx, RecordStruct v) {
if (ctx.context.isFieldEmpty("Credentials") && (v == null))
return null;
// no need to do anything if the creds are the same
if (!ctx.context.isFieldEmpty("Credentials") && (v != null) && ctx.getCredentials().equals(v)) // TODO the equals is not right for deep records
return null;
// when credentials change you are left with only guest access
return ctx.toBuilder().withCredentials(v);
}
// instance code
protected RecordStruct context = null;
/**
* @return the id of the user, if guest then "00000_000000000000002", if root then "00000_000000000000001"
*/
public String getUserId() {
return this.context.getFieldAsString("UserId");
}
/**
* @return the username of the user
*/
public String getUsername() {
return this.context.getFieldAsString("Username");
}
/**
* @return the fullname of the user
*/
public String getFullName() {
return this.context.getFieldAsString("FullName");
}
/**
* @return the primary email of the user
*/
public String getEmail() {
return this.context.getFieldAsString("Email");
}
/**
* @return the authentication token
*/
public String getAuthToken() {
return this.context.getFieldAsString("AuthToken");
}
public RecordStruct getCredentials() {
return this.context.getFieldAsRecord("Credentials");
}
/**
* Verified users have been authenticated or are the built-in Guest or Root users.
*
* @return true if user does not require verification
*/
public boolean isVerified() {
return this.context.getFieldAsBooleanOrFalse("Verified");
}
/**
* Authenticated users have been authenticated by dcAuth service.
* Unlike isVerified we do not return true for the built-in Guest or Root users.
*
* @return true if user was authenticated via credentials
*/
public boolean isAuthenticated() {
return !this.context.isFieldEmpty("AuthToken");
}
/**
* @return chronology to use with this task (for output)
*/
public String getChronology() {
return this.context.getFieldAsString("Chronology");
}
/**
* @return locale to use with this task for output
*/
public String getLocale() {
return this.context.getFieldAsString("Locale");
}
/**
* Domain indicates the domain targeted by a request.
*
* @return domain string
*/
public String getDomainId() {
return this.context.getFieldAsString("DomainId");
}
public DomainInfo getDomain() {
return Hub.instance.getDomainInfo(this.getDomainId());
}
// only use during booting
protected UserContext() {
this.context = new RecordStruct();
}
/**
* @param m create a task context from a message (RPC calls to dcBus), keep in mind
* this is info gathering only, message must not be allowed to force an
* authenticated/elevated state inappropriately - from RPC clear "Elevated"
* field before calling this
*/
protected UserContext(RecordStruct m) {
this.context = m;
}
public RecordStruct freezeToRecord() {
return (RecordStruct) this.context.deepCopy();
}
public OperationContextBuilder toBuilder() {
return new OperationContextBuilder(this.freezeToRecord());
}
/**
* @param m store task context into a message - for context transfer over bus
*/
public void freeze(RecordStruct m) {
m.copyFields(this.context);
}
/**
* @param m store task context into a message - for context transfer over bus
*/
public void freezeRpc(RecordStruct m) {
// rpc doesn't get creds or token
m.copyFields(this.context, "Credentials", "AuthToken");
}
public boolean looksLikeGuest() {
if (!"00000_000000000000002".equals(this.getUserId()))
return false;
if (!this.context.isFieldEmpty("AuthToken") || !this.context.isFieldEmpty("Credentials"))
return false;
ListStruct creds = this.context.getFieldAsList("AuthTags");
if ((creds == null) || (creds.getSize() != 1))
return false;
return ("Guest".equals(creds.getItemAsString(0)));
}
public boolean looksLikeRoot() {
if (!"00000_000000000000001".equals(this.getUserId()))
return false;
if (!this.context.isFieldEmpty("AuthToken") || !this.context.isFieldEmpty("Credentials"))
return false;
ListStruct creds = this.context.getFieldAsList("AuthTags");
if ((creds == null) || (creds.getSize() == 0))
return false;
for (int i = 0; i < creds.getSize(); i++) {
if ("SysAdmin".equals(creds.getItemAsString(i)))
return true;
}
return false;
}
/**
* @param tags to search for with this user
* @return true if this user has one of the requested authorization tags (does not check authentication)
*/
public boolean isTagged(String... tags) {
ListStruct creds = this.context.getFieldAsList("AuthTags");
if (creds == null)
return false;
for (int i = 0; i < creds.getSize(); i++) {
String has = creds.getItemAsString(i);
for (String wants : tags) {
if (has.equals(wants))
return true;
}
}
return false;
}
@Override
public String toString() {
RecordStruct usr = (RecordStruct) this.context.deepCopy();
RecordStruct creds = usr.getFieldAsRecord("Credentials");
if (creds != null) {
if (creds.hasField("Password"))
creds.setField("Password", "*****");
}
return usr.toPrettyString();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof UserContext))
return false;
return this.context.equals(((UserContext)obj).context);
}
public void freezeSafe(RecordStruct m) {
m.copyFields(this.context);
RecordStruct creds = m.getFieldAsRecord("Credentials");
if (creds != null) {
if (creds.hasField("Password"))
creds.setField("Password", "*****");
}
}
}