prep to push
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Observable;
|
||||
|
||||
/** A more or less virtual collection of items.
|
||||
* this object is a suitable holder of resultsets. it will be overridable so
|
||||
* we can create specialized virtual holders that use backends. by itself it will
|
||||
* implement in memory list.
|
||||
* also this class is an observable and we can monitor update to it.
|
||||
*/
|
||||
public class Bag<E> extends Observable implements Collection<E>{
|
||||
/** event to send to observers. */
|
||||
public static final class BagChanged<E>{
|
||||
public static final int ADD=0;
|
||||
public static final int REMOVE=1;
|
||||
public static final int ACCESS=2;
|
||||
public static final int POST_LOAD=3;
|
||||
public static final int PRE_SAVE=4;
|
||||
final Bag<E> bag;
|
||||
final int operation;
|
||||
final Object[] arguments;
|
||||
public BagChanged(Bag<E> p,int op,Object ... args){
|
||||
bag=p;
|
||||
operation=op;
|
||||
arguments=args;
|
||||
}
|
||||
public Bag<E> getBag() {
|
||||
return bag;
|
||||
}
|
||||
public int getOperation() {
|
||||
return operation;
|
||||
}
|
||||
public Object[] getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
}
|
||||
final ArrayList<E> items=new ArrayList<>();
|
||||
|
||||
public Bag(){
|
||||
}
|
||||
public Bag(Iterable<E> o){
|
||||
this(o.iterator());
|
||||
}
|
||||
public Bag(Iterator<E> o){
|
||||
while(o.hasNext()) add(o.next());
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size()==0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
final Iterator<E> it=iterator();
|
||||
while(it.hasNext()){
|
||||
final E e=it.next();
|
||||
if(e!=null && o!=null && e.equals(o)) return true;
|
||||
else if(e==o) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
for (Object e : c) if (!contains(e)) return false;
|
||||
return true;
|
||||
}
|
||||
public ListIterator<E> listIterator(){
|
||||
return listIterator(0);
|
||||
}
|
||||
public ListIterator<E> listIterator(int offset){
|
||||
return items.listIterator(offset);
|
||||
}
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return items.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return toArray(new Object[size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return items.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
if(items.contains(e)) return true;
|
||||
if(countObservers()>0){
|
||||
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.ADD,e);
|
||||
setChanged();
|
||||
notifyObservers(evt);
|
||||
}
|
||||
return items.add(e);
|
||||
}
|
||||
public Bag<E> append(E e){
|
||||
add(e);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
if(!contains(o)) return false;
|
||||
if(countObservers()>0){
|
||||
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.REMOVE,o);
|
||||
setChanged();
|
||||
notifyObservers(evt);
|
||||
}
|
||||
return items.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> c) {
|
||||
if(countObservers()>0){
|
||||
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.ADD,c.toArray());
|
||||
setChanged();
|
||||
notifyObservers(evt);
|
||||
}
|
||||
if(c==null || c.size()==0) return false;
|
||||
c.forEach(e->{this.append(e);});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
if(countObservers()>0){
|
||||
BagChanged<E> evt=new Bag.BagChanged<>(this,BagChanged.REMOVE,c!=null?c.toArray():null);
|
||||
setChanged();
|
||||
notifyObservers(evt);
|
||||
}
|
||||
if(c!=null){
|
||||
return items.removeAll(c);
|
||||
}else{
|
||||
items.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return items.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
removeAll(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -9,78 +16,93 @@ public class Check implements Iterable<Check> {
|
||||
public static abstract class Op{
|
||||
public abstract boolean met(Check c,Object val);
|
||||
}
|
||||
/** logical AND operation. */
|
||||
public static Op AND=new Op(){
|
||||
public String toString(){return "AND";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** logical OR operation. */
|
||||
public static Op OR=new Op(){
|
||||
public String toString(){return "OR";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** logical NOT operation. */
|
||||
public static Op NOT=new Op(){
|
||||
public String toString(){return "NOT";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** arithmetic equal test. */
|
||||
public static Op EQ=new Op(){
|
||||
public String toString(){return "=";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** arithmetic negated equal test. */
|
||||
public static Op NEQ=new Op(){
|
||||
public String toString(){return "<>";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** greater than check. */
|
||||
public static Op GT=new Op(){
|
||||
public String toString(){return ">";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** greater than or equal check. */
|
||||
public static Op GTE=new Op(){
|
||||
public String toString(){return ">=";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** less than check. */
|
||||
public static Op LT=new Op(){
|
||||
public String toString(){return "<";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** less than or equal check. */
|
||||
public static Op LTE=new Op(){
|
||||
public String toString(){return "<=";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** like check case insensitive. */
|
||||
public static Op LIKE=new Op(){
|
||||
public String toString(){return "LIKE";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** set membership check. */
|
||||
public static Op IN=new Op(){
|
||||
public String toString(){return "IN";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** negated set membership check. */
|
||||
public static Op NOT_IN=new Op(){
|
||||
public String toString(){return "NOT IN";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/** iterator over checks.
|
||||
*
|
||||
*/
|
||||
public static class CheckIterator implements Iterator<Check>{
|
||||
final Check root;
|
||||
Check cur;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -53,15 +60,16 @@ public class Entity extends Hdr{
|
||||
* @param cls
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Entity publish(Class<? extends DBO> cls){
|
||||
Entity ret=registry.get(cls.getSimpleName());
|
||||
if(ret!=null) return ret;
|
||||
//System.out.println("Analyzing:"+cls);
|
||||
Class base=cls.getSuperclass();
|
||||
Class<?> base=cls.getSuperclass();
|
||||
Entity base_ent=null;
|
||||
int position0=0;
|
||||
if(base!=null && base!=DBO.class){
|
||||
base_ent=publish(base);
|
||||
base_ent=publish((Class<? extends DBO>)base);
|
||||
position0=base_ent.count();
|
||||
}
|
||||
java.lang.reflect.Field[] declaredFields = cls.getDeclaredFields();
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -207,8 +214,8 @@ public final class SQL implements Appendable{
|
||||
/** fills check values from dbo record where equal and not-equal are used.
|
||||
* we place this method here to be as close as possible to the one which generates the sql code.
|
||||
* check and check_import must be in synch.
|
||||
* @param filter
|
||||
* @param params
|
||||
* @param filter set of checks
|
||||
* @param rec record to check
|
||||
*/
|
||||
public final void check_import(Check filter,DBO rec) {
|
||||
if(filter.isLeaf()){
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -11,7 +18,7 @@ import com.reliancy.dbo.Action.Load;
|
||||
|
||||
|
||||
/** SQLIterator will delay closing a connection and will iterate over result set.
|
||||
*
|
||||
* TODO: no support for orderby yet
|
||||
*/
|
||||
public class SQLReader implements SiphonIterator<DBO>{
|
||||
protected final Entity entity;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.Closeable;
|
||||
@@ -172,7 +179,14 @@ public class SQLWriter implements Closeable{
|
||||
if(rec.getStatus()==DBO.Status.USED){
|
||||
stmt=updateStmt;
|
||||
// update has a pk condition for sure
|
||||
stmt.setObject(supplied.size()+1,pk.get(rec,null),terminal.getTypeId(pk.getType(),pk.getTypeParams()));
|
||||
Object pkval=pk.get(rec,null);
|
||||
if(Handy.isEmpty(pkval)) throw new SQLException("Used object with empty PK");
|
||||
//System.out.println("UPDT:"+stmt+"/"+pkval);
|
||||
stmt.setObject(
|
||||
supplied.size()+1,
|
||||
pkval,
|
||||
terminal.getTypeId(pk.getType(),pk.getTypeParams())
|
||||
);
|
||||
}
|
||||
if(stmt==null) return false;
|
||||
// copy values
|
||||
@@ -199,7 +213,7 @@ public class SQLWriter implements Closeable{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else
|
||||
if(rec.getStatus()==DBO.Status.USED){
|
||||
this.itemsUpdated+=ucode;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -29,7 +36,7 @@ public interface Terminal {
|
||||
Entity ent=Entity.recall(cls);
|
||||
String sig="/"+ent.getName()+"/load";
|
||||
try(Action act=begin(sig).load(ent).limit(1).if_pk(id).execute()){
|
||||
return (T)act.first();
|
||||
return cls.cast(act.first());
|
||||
}
|
||||
}
|
||||
public default DBO load(Entity ent,Object...id) throws IOException {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||
import com.reliancy.util.CodeException;
|
||||
import com.reliancy.util.ResultCode;
|
||||
|
||||
/** Base Application class from where specific launchers derive.
|
||||
* Derived classes will usually bring in jetty or tomcat or some other launch ability.
|
||||
*/
|
||||
public abstract class App extends Processor{
|
||||
public static int ERR_NOCONFIG=ResultCode.defineFailure(0x01,App.class,"config missing. provide at least empty one.");
|
||||
public static int ERR_NOTCLOSED=ResultCode.defineFailure(0x02,App.class,"unbalanced call. resource called twice:${resource}");
|
||||
protected Processor first=null;
|
||||
protected Processor last=null;
|
||||
protected RoutedEndPoint router=null;
|
||||
protected SecurityPolicy policy=null;
|
||||
|
||||
public App(String id) {
|
||||
super(id);
|
||||
}
|
||||
public void before(Request request,Response response) throws IOException{
|
||||
}
|
||||
public void after(Request request,Response response) throws IOException{
|
||||
}
|
||||
public void serve(Request req,Response resp) throws IOException{
|
||||
if(first!=null) first.process(req, resp);
|
||||
if(router!=null) router.process(req,resp);
|
||||
}
|
||||
public <T extends Processor> T addProcessor(T m){
|
||||
if(first==null){
|
||||
last=first=m;
|
||||
}else{
|
||||
last.next=m;
|
||||
}
|
||||
while(last.next!=null) last=last.next;
|
||||
return m;
|
||||
}
|
||||
public void removeProcessor(Processor m){
|
||||
if(first==m){
|
||||
if(first==last) last=null;
|
||||
first=first.next;
|
||||
while(last!=null && last.next!=null) last=last.next;
|
||||
}else{
|
||||
for(Processor prev=first;prev!=null;prev=prev.next){
|
||||
if(prev.next==m){
|
||||
if(last==m) last=prev;
|
||||
prev.next=m.next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m.next=null;
|
||||
}
|
||||
public Processor getProcessor(String id){
|
||||
for(Processor c=first;c!=null;c=c.next){
|
||||
if(c.getId().equalsIgnoreCase(id)) return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public RoutedEndPoint getRouter() {
|
||||
return router;
|
||||
}
|
||||
public void setRouter(RoutedEndPoint router) {
|
||||
this.router = router;
|
||||
}
|
||||
public void run(Config conf) throws Exception {
|
||||
try{
|
||||
begin(conf);
|
||||
work();
|
||||
}finally{
|
||||
end();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void begin(Config conf) throws Exception{
|
||||
if(config!=null) throw new CodeException(ERR_NOTCLOSED).put("resource","Router.begin()");
|
||||
if(conf==null) throw new CodeException(ERR_NOCONFIG);
|
||||
config=conf;
|
||||
for(Processor p=first;p!=null;p=p.getNext()){
|
||||
p.begin(config);
|
||||
}
|
||||
if(router!=null) router.begin(config);
|
||||
}
|
||||
@Override
|
||||
public void end() throws Exception{
|
||||
if(router!=null) router.end();
|
||||
for(Processor p=first;p!=null;p=p.getNext()){
|
||||
p.end();
|
||||
}
|
||||
super.end();
|
||||
log().info("stopping app:"+getId());
|
||||
}
|
||||
public AppSessionFilter addAppSession(){
|
||||
return addProcessor(new AppSessionFilter(this));
|
||||
}
|
||||
public AppSessionFilter addAppSession(AppSession.Factory f){
|
||||
return addProcessor(new AppSessionFilter(this,f));
|
||||
}
|
||||
public SecurityPolicy setSecurityPolicy(SecurityPolicy secpol){
|
||||
if(secpol==policy) return secpol;
|
||||
if(policy!=null){
|
||||
MethodDecorator.retract(policy);
|
||||
removeProcessor(policy);
|
||||
}
|
||||
policy=secpol;
|
||||
if(policy!=null){
|
||||
addProcessor(policy);
|
||||
MethodDecorator.publish(policy); // register security policy as decorator factory
|
||||
}
|
||||
return secpol;
|
||||
}
|
||||
public SecurityPolicy getSecurityPolicy(){
|
||||
return policy;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,33 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.reliancy.jabbasec.SecurityActor;
|
||||
import com.reliancy.jabba.sec.SecurityActor;
|
||||
import com.reliancy.jabba.ui.Feedback;
|
||||
|
||||
public class AppSession implements Session{
|
||||
public static interface Factory{
|
||||
AppSession create(String id,App app);
|
||||
}
|
||||
final String id;
|
||||
final HashMap<String,Object> values;
|
||||
final App app;
|
||||
final HashMap<String,Object> values=new HashMap<>();
|
||||
long timeCreated;
|
||||
long lastActive;
|
||||
long maxAge;
|
||||
SecurityActor user;
|
||||
|
||||
public AppSession(String id){
|
||||
Feedback feedback;
|
||||
|
||||
public AppSession(String id,App app){
|
||||
this.id=id;
|
||||
values=new HashMap<>();
|
||||
this.app=app;
|
||||
lastActive=timeCreated=System.currentTimeMillis();
|
||||
maxAge=1000*60*15;
|
||||
}
|
||||
@@ -27,6 +40,9 @@ public class AppSession implements Session{
|
||||
public Object getValue(String key) {
|
||||
return values.get(key);
|
||||
}
|
||||
public App getApp(){
|
||||
return app;
|
||||
}
|
||||
public long getTimeInactive(){
|
||||
return System.currentTimeMillis()-lastActive;
|
||||
}
|
||||
@@ -64,4 +80,12 @@ public class AppSession implements Session{
|
||||
public void setUser(SecurityActor user){
|
||||
this.user=user;
|
||||
}
|
||||
public static AppSession getInstance() {
|
||||
CallSession ss=CallSession.getInstance();
|
||||
return ss!=null?(AppSession)ss.getAppSession():null;
|
||||
}
|
||||
public Feedback getFeedback() {
|
||||
if(feedback==null) feedback=new Feedback();
|
||||
return feedback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -9,8 +16,16 @@ import java.util.UUID;
|
||||
*/
|
||||
public class AppSessionFilter extends Processor{
|
||||
public static final String KEY_NAME="jbssid";
|
||||
public AppSessionFilter() {
|
||||
super(AppSessionFilter.class.getSimpleName().toLowerCase());
|
||||
AppSession.Factory factory;
|
||||
App app;
|
||||
public AppSessionFilter(App a) {
|
||||
this(a,null);
|
||||
}
|
||||
public AppSessionFilter(App a,AppSession.Factory f) {
|
||||
super(AppSessionFilter.class.getSimpleName());
|
||||
app=a;
|
||||
if(f==null) f=(id,app)->new AppSession(id, app);
|
||||
factory=f;
|
||||
}
|
||||
@Override
|
||||
public void before(Request request, Response response) throws IOException {
|
||||
@@ -23,7 +38,7 @@ public class AppSessionFilter extends Processor{
|
||||
if(ss!=null){
|
||||
if(ss.isExpired()){
|
||||
// this app sessin expired - create a new one
|
||||
ss=new AppSession(ssid);
|
||||
ss=factory.create(ssid,app);
|
||||
AppSession.setInstance(ssid, ss);
|
||||
}else{
|
||||
// this session is good
|
||||
@@ -31,7 +46,7 @@ public class AppSessionFilter extends Processor{
|
||||
}
|
||||
}else{
|
||||
// no session available
|
||||
ss=new AppSession(ssid);
|
||||
ss=factory.create(ssid,app);
|
||||
AppSession.setInstance(ssid, ss);
|
||||
}
|
||||
CallSession css=CallSession.getInstance();
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -71,11 +78,11 @@ public class CallSession implements Session{
|
||||
int len=callers.size();
|
||||
return len>0?callers.get(len-1):null;
|
||||
}
|
||||
public static ThreadLocal<CallSession> instance=new ThreadLocal<>();
|
||||
/**
|
||||
* Will return current session given the call stack.
|
||||
* @return
|
||||
* @return thread local call session
|
||||
*/
|
||||
public static ThreadLocal<CallSession> instance=new ThreadLocal<>();
|
||||
public static CallSession getInstance(){
|
||||
CallSession ret=instance.get();
|
||||
if(ret==null) instance.set(ret=new CallSession());
|
||||
|
||||
@@ -1,9 +1,40 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public interface Config {
|
||||
public static class Property<V> {
|
||||
|
||||
final String name;
|
||||
final Class<V> typ;
|
||||
public Property(String name,Class<V> typ){
|
||||
this.name=name;
|
||||
this.typ=typ;
|
||||
}
|
||||
public String getName(){return name;}
|
||||
public Class<V> getTyp(){return typ;}
|
||||
public V get(Config store,V def){
|
||||
return store.getProperty(this,def);
|
||||
}
|
||||
public V get(Config store){
|
||||
return get(store,null);
|
||||
}
|
||||
public void set(Config store,V val){
|
||||
store.setProperty(this, val);
|
||||
}
|
||||
}
|
||||
public static final Property<String> LOG_LEVEL=new Property<String>("LOG_LEVEL",String.class);
|
||||
public static final Property<Logger> LOGGER=new Property<Logger>("LOGGER",Logger.class);
|
||||
|
||||
public void load();
|
||||
public void save();
|
||||
public String getId();
|
||||
public Object getProperty(String key,Object def);
|
||||
public Config setProperty(String key,Object val);
|
||||
public <T> Config setProperty(Property<T> key,T val);
|
||||
public <T> T getProperty(Property<T> key,T def);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class FileConfig implements Config{
|
||||
String path;
|
||||
final HashMap<String,Object> props=new HashMap<>();
|
||||
public FileConfig(String p){
|
||||
path=p;
|
||||
load();
|
||||
}
|
||||
public FileConfig(){
|
||||
this(null);
|
||||
}
|
||||
public void clear(){
|
||||
props.clear();
|
||||
}
|
||||
@Override
|
||||
public void load() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setId(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
@Override
|
||||
public <T> T getProperty(Config.Property<T> key, T def) {
|
||||
if(props.containsKey(key.getName())) return key.getTyp().cast(props.get(key.getName()));
|
||||
else return def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Config setProperty(Config.Property<T> key, T val) {
|
||||
props.put(key.getName(),val);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
import com.reliancy.util.Resources;
|
||||
import java.io.File;
|
||||
@@ -40,7 +47,7 @@ public class FileServer extends EndPoint implements Resources.PathRewrite{
|
||||
@Override
|
||||
public void serve(Request request, Response response) throws IOException {
|
||||
String path=request.getPath();
|
||||
log().info("serving:"+path);
|
||||
log().debug("to serve:"+path);
|
||||
for(String prefix:map.keySet()){
|
||||
boolean match=path.startsWith(prefix);
|
||||
if(match){
|
||||
@@ -49,13 +56,14 @@ public class FileServer extends EndPoint implements Resources.PathRewrite{
|
||||
if(!filt.get(prefix).isAcceptable(rpath)) continue; // not acceptable to filter
|
||||
URL f=Resources.findFirst(this, rpath, sp);
|
||||
if(f==null) continue; // skip if rpath not located
|
||||
System.out.println("RES:"+f);
|
||||
this.log().debug("\tfound:"+f);
|
||||
writeResource(f,response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
response.setStatus(Response.HTTP_NOT_FOUND);
|
||||
response.getEncoder().writeln("missing file:{0}",path);
|
||||
this.log().error("not found:"+path);
|
||||
}
|
||||
/**
|
||||
* we prefix our path for disk and class contexts.
|
||||
@@ -111,7 +119,7 @@ public class FileServer extends EndPoint implements Resources.PathRewrite{
|
||||
public Iterator<String> enumRoutes(){
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
public void exportRoutes(RouterEndPoint rep) {
|
||||
public void exportRoutes(RoutedEndPoint rep) {
|
||||
streamRoutes().forEach(up->rep.addRoute("GET",up+".*",this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EventListener;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.reliancy.jabba.sec.NotAuthentic;
|
||||
import com.reliancy.jabba.sec.Secured;
|
||||
import com.reliancy.jabba.sec.SecurityActor;
|
||||
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||
import com.reliancy.jabba.sec.plain.PlainSecurityStore;
|
||||
import com.reliancy.jabba.ui.Feedback;
|
||||
import com.reliancy.jabba.ui.FeedbackLine;
|
||||
import com.reliancy.jabba.ui.Menu;
|
||||
import com.reliancy.jabba.ui.MenuItem;
|
||||
import com.reliancy.jabba.ui.Rendering;
|
||||
import com.reliancy.jabba.ui.Template;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Router is entry point and servlet implementation that dispatches messages to our endpoints.
|
||||
* It will launch an embedded jetty server.
|
||||
* It will provide facilities to register endpoints.
|
||||
*/
|
||||
public class JettyApp extends App implements Handler{
|
||||
enum State{
|
||||
STOPPED,
|
||||
FAILED,
|
||||
STARTING,
|
||||
STARTED,
|
||||
STOPPING,
|
||||
RUNNING
|
||||
}
|
||||
protected Connector[] connectors;
|
||||
protected Server jetty;
|
||||
private volatile State _state;
|
||||
|
||||
public JettyApp() {
|
||||
super("JettyApp");
|
||||
jetty = new Server();
|
||||
jetty.setHandler(this);
|
||||
_state=State.STOPPED;
|
||||
}
|
||||
/** implementation of jetty handler interface */
|
||||
@Override
|
||||
public Server getServer() {
|
||||
return jetty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServer(Server arg0) {
|
||||
jetty=arg0;
|
||||
}
|
||||
@Override
|
||||
public boolean addEventListener(EventListener arg0) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean removeEventListener(EventListener arg0) {
|
||||
return false;
|
||||
}
|
||||
protected void setState(State s){
|
||||
_state=s;
|
||||
}
|
||||
@Override
|
||||
public boolean isFailed() {
|
||||
return _state==State.FAILED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return _state==State.RUNNING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
return _state==State.STARTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarting() {
|
||||
return _state==State.STARTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStopped() {
|
||||
return _state==State.STOPPED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStopping() {
|
||||
return _state==State.STOPPING;
|
||||
}
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
_state=State.STARTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
_state=State.STOPPED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
/**
|
||||
* Our implementation of a handle process.
|
||||
* In case of exception if we can locate /tempaltes/error.hbs we use it else we re-throw.
|
||||
*/
|
||||
@Override
|
||||
public void handle(String target,
|
||||
Request baseRequest,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
com.reliancy.jabba.Request req=new com.reliancy.jabba.Request(request);
|
||||
Response resp=new Response(response);
|
||||
|
||||
CallSession ss=CallSession.getInstance();
|
||||
try{
|
||||
ss.begin(null, req, resp);
|
||||
process(req,resp);
|
||||
}catch(IOException ioex){
|
||||
Template t=Template.find("/templates/error.hbs");
|
||||
if(t==null) throw ioex;
|
||||
Rendering.begin(t)
|
||||
.with(ioex)
|
||||
.end(resp.getEncoder().getWriter());
|
||||
log().error("error:",ioex);
|
||||
}catch(RuntimeException rex){
|
||||
Template t=Template.find("/templates/error.hbs");
|
||||
if(t==null) throw rex;
|
||||
Rendering.begin(t)
|
||||
.with(rex)
|
||||
.end(resp.getEncoder().getWriter());
|
||||
log().error("error:",rex);
|
||||
}finally{
|
||||
ss.end();
|
||||
}
|
||||
}
|
||||
/** our own interface specific to jetty engine*/
|
||||
|
||||
public Connector[] getConnectors(){
|
||||
if(connectors!=null) return connectors;
|
||||
ServerConnector connector = new ServerConnector(jetty);
|
||||
connector.setReuseAddress(false);
|
||||
connector.setPort(8090);
|
||||
connectors=new Connector[] {connector};
|
||||
return connectors;
|
||||
}
|
||||
public void begin(Config conf) throws Exception{
|
||||
super.begin(conf);
|
||||
jetty.setConnectors(getConnectors());
|
||||
try{
|
||||
jetty.start();
|
||||
}catch(Exception ex){
|
||||
setState(State.FAILED);
|
||||
if(ex.getCause() instanceof java.net.BindException){
|
||||
log().error("Bind issue",ex);
|
||||
Thread.sleep(3000);
|
||||
}else throw ex;
|
||||
}
|
||||
}
|
||||
public void work() throws InterruptedException{
|
||||
setState(State.RUNNING);
|
||||
if(jetty!=null) jetty.join();
|
||||
}
|
||||
public void end() throws Exception{
|
||||
//setState(State.STOPPING);
|
||||
super.end();
|
||||
Connector[] connectors=jetty.getConnectors();
|
||||
// System.out.println(connectors);
|
||||
if(connectors!=null) for(Connector c:connectors){
|
||||
ServerConnector cc=(ServerConnector) c;
|
||||
//System.out.println("stopping connecor:"+cc);
|
||||
try{
|
||||
cc.stop();
|
||||
cc.getConnectedEndPoints().forEach((endpoint)-> {
|
||||
//System.out.println("closing endpoint:"+endpoint);
|
||||
endpoint.close();
|
||||
});
|
||||
}finally{
|
||||
cc.close();
|
||||
//System.out.println("closing connecor:"+cc.getState());
|
||||
}
|
||||
}
|
||||
//System.out.println("signaling...");
|
||||
jetty.stop();
|
||||
//setState(State.STOPPED);
|
||||
//System.out.println("cleanup...");
|
||||
System.gc();
|
||||
//System.out.println("return...");
|
||||
}
|
||||
public static void main( String[] args ) throws Exception{
|
||||
//System.out.println("Hello World!");
|
||||
Template.search_path("./var",App.class);
|
||||
JettyApp app=new JettyApp();
|
||||
app.addAppSession();
|
||||
SecurityPolicy secpol=new SecurityPolicy().setStore(new PlainSecurityStore());
|
||||
app.setSecurityPolicy(secpol);
|
||||
RoutedEndPoint rep=new RoutedEndPoint().importMethods(app);
|
||||
app.setRouter(rep);
|
||||
FileServer fs=new FileServer("/static","./var/public");
|
||||
fs.exportRoutes(app.getRouter());
|
||||
Menu top_menu=Menu.request(Menu.TOP);
|
||||
top_menu.add(new MenuItem("home")).addSpacer().add(new MenuItem("login"));
|
||||
top_menu.setTitle("Jabba");
|
||||
app.run(new FileConfig());
|
||||
//System.out.println("Goodbye World!");
|
||||
}
|
||||
|
||||
@Routed()
|
||||
public String hello(){
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
context.put("name", "Jared");
|
||||
String ret="";
|
||||
try {
|
||||
Template t=Template.find("/templates/login.hbs");
|
||||
System.out.println("Template:"+t);
|
||||
ret = t.render(context).toString();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
//#return "Hello World";
|
||||
}
|
||||
@Routed(
|
||||
path="/helloPlain"
|
||||
)
|
||||
public void hello2(com.reliancy.jabba.Request req,Response resp) throws IOException{
|
||||
resp.getEncoder().writeln("Hi There");
|
||||
}
|
||||
@Routed(
|
||||
path="/hello3/{idd:int}"
|
||||
)
|
||||
public String hello3(int id){
|
||||
return "Hello3:"+id;
|
||||
}
|
||||
@Routed(
|
||||
path="/"
|
||||
)
|
||||
public String home(){
|
||||
StringBuilder buf=new StringBuilder();
|
||||
buf.append("<p>Sample pages:</p>");
|
||||
buf.append("<dd><a href='/helloPlain'>plain</a></dd>");
|
||||
buf.append("<dd><a href='/hello3/5'>parametric</a></dd>");
|
||||
buf.append("<dd><a href='/hello'>templated</a></dd>");
|
||||
buf.append("<dd><a href='/secured'>secured http</a></dd>");
|
||||
buf.append("<dd><a href='/secured_form'>secured form</a></dd>");
|
||||
return buf.toString();
|
||||
}
|
||||
@Routed
|
||||
@Secured
|
||||
public String secured(){
|
||||
return "We are secured";
|
||||
}
|
||||
@Routed
|
||||
@Secured(
|
||||
login_form = "/login"
|
||||
)
|
||||
public String secured_form(){
|
||||
return "We are secured by form";
|
||||
}
|
||||
@Routed
|
||||
public void login(com.reliancy.jabba.Request req,Response resp){
|
||||
//return "login form here";
|
||||
if(req.getVerb().equals("POST")){
|
||||
// here we need to process login and redirect
|
||||
try{
|
||||
System.out.println("Post login");
|
||||
String userid=(String)req.getParam("userid",null);
|
||||
String pwd=(String)req.getParam("password",null);
|
||||
AppSession ass=AppSession.getInstance();
|
||||
System.out.println("SS:"+ass);
|
||||
System.out.println("P:"+userid+"/"+pwd);
|
||||
SecurityPolicy secpol=ass.getApp().getSecurityPolicy();
|
||||
SecurityActor user=secpol.authenticate(userid, pwd);
|
||||
if(user==null) throw new NotAuthentic("invalid credentials");
|
||||
resp.setStatus(Response.HTTP_FOUND_REDIRECT);
|
||||
//String old_url=request.getPath();
|
||||
//old_url=URLEncoder.encode(old_url,StandardCharsets.UTF_8.toString());
|
||||
resp.setHeader("Location","/home");
|
||||
}catch(Exception ex){
|
||||
log().error("error:",ex);
|
||||
Feedback.get().push(FeedbackLine.error(ex.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
//Map<String, Object> context = new HashMap<>();
|
||||
//context.put("app_title", "Jabba Login");
|
||||
//context.put("name", "Jared");
|
||||
//ArrayList<FeedbackLine> events=new ArrayList<>();
|
||||
|
||||
//Feedback.get().push(FeedbackLine.error("Error"));
|
||||
//Feedback.get().push(FeedbackLine.info("Error"));
|
||||
//Feedback.get().push(FeedbackLine.warn("Error"));
|
||||
//context.put("feedback",events);
|
||||
try {
|
||||
resp.setContentType("text/html");
|
||||
Rendering.begin("/templates/login.hbs")
|
||||
//.with("feedback",events)
|
||||
.end(resp.getEncoder().getWriter());
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** Decorator is a handler for method annotations.
|
||||
* The name is borrowed from python because it connects java annotations to handler that are called before and after a method is invoked.
|
||||
* Decorator itself is injected and called by methodendpoint as a filter but the factory does not have to return an object.
|
||||
* it can just use the methodendpoint to register it somewhere once during startup.
|
||||
*/
|
||||
public abstract class MethodDecorator {
|
||||
public static interface Factory{
|
||||
MethodDecorator assertDecorator(MethodEndPoint mep,Annotation ann);
|
||||
}
|
||||
static final ArrayList<MethodDecorator.Factory> registry=new ArrayList<>();
|
||||
public static void publish(MethodDecorator.Factory d){
|
||||
if(!registry.contains(d)) registry.add(d);
|
||||
}
|
||||
public static void retract(MethodDecorator.Factory d){
|
||||
while (registry.remove(d));
|
||||
}
|
||||
public static MethodDecorator query(MethodEndPoint mep,Annotation ann){
|
||||
for(MethodDecorator.Factory f:registry){
|
||||
MethodDecorator d=f.assertDecorator(mep, ann);
|
||||
if(d!=null) return d;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
MethodEndPoint method;
|
||||
Annotation annotation;
|
||||
public MethodDecorator(MethodEndPoint mep,Annotation ann){
|
||||
method=mep;
|
||||
annotation=ann;
|
||||
}
|
||||
public abstract void beforeMethod(Request request, Response response);
|
||||
public abstract void afterMethod(Request request, Response response);
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
@@ -12,16 +21,17 @@ public class MethodEndPoint extends EndPoint{
|
||||
FULL, // one or more arguments need to do casting
|
||||
|
||||
}
|
||||
Route route;
|
||||
Routed route;
|
||||
Object target;
|
||||
Method method;
|
||||
Parameter[] params;
|
||||
Class<?> retType;
|
||||
InvokeProfile invokeType;
|
||||
|
||||
public MethodEndPoint(Object target,Method m,Route r) {
|
||||
ArrayList<MethodDecorator> decorators=new ArrayList<>();
|
||||
|
||||
public MethodEndPoint(Object target,Method m) {
|
||||
super(target.getClass().getSimpleName()+"."+m.getName());
|
||||
this.route=r;
|
||||
this.route=m.getAnnotation(Routed.class);
|
||||
this.target=target;
|
||||
this.method=m;
|
||||
this.params=m.getParameters();
|
||||
@@ -33,8 +43,27 @@ public class MethodEndPoint extends EndPoint{
|
||||
if(params.length==0){
|
||||
invokeType=InvokeProfile.NOARG;
|
||||
}
|
||||
bindDecorators();
|
||||
}
|
||||
public String getVerb(){
|
||||
return route.verb();
|
||||
}
|
||||
public String getPath() {
|
||||
String ret=route.path();
|
||||
if(!ret.startsWith("/")) ret="/"+ret;
|
||||
ret=ret.replace("{method}",method.getName());
|
||||
return ret;
|
||||
}
|
||||
public Routed getRoute(){
|
||||
return route;
|
||||
}
|
||||
/** pulls in and adds decorator filters to this methodcall that are supported. */
|
||||
protected final void bindDecorators(){
|
||||
for(Annotation a:method.getAnnotations()){
|
||||
MethodDecorator d=MethodDecorator.query(this,a);
|
||||
if(d!=null) decorators.add(d);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serve(Request request, Response response) throws IOException{
|
||||
log().info("Serving method....{}",invokeType);
|
||||
@@ -50,8 +79,7 @@ public class MethodEndPoint extends EndPoint{
|
||||
encodeResponse(ret,response);
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
// here we do full unmarshalling, marshalling
|
||||
default:{ // here we do full unmarshalling, marshalling
|
||||
Object[] argVals=decodeRequest(request);
|
||||
ret=method.invoke(target,argVals);
|
||||
encodeResponse(ret,response);
|
||||
@@ -62,12 +90,6 @@ public class MethodEndPoint extends EndPoint{
|
||||
else throw new IOException(ex2);
|
||||
}
|
||||
}
|
||||
public String getPath() {
|
||||
String ret=route.path();
|
||||
if(!ret.startsWith("/")) ret="/"+ret;
|
||||
ret=ret.replace("{method}",method.getName());
|
||||
return ret;
|
||||
}
|
||||
protected Object[] decodeRequest(Request request){
|
||||
Object[] argVals=new Object[params.length];
|
||||
for(int i=0;i<argVals.length;i++){
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
/** uri path decoded into tokens. */
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
import java.io.IOException;
|
||||
import org.slf4j.Logger;
|
||||
@@ -7,7 +14,7 @@ public abstract class Processor {
|
||||
protected Processor next;
|
||||
protected String id;
|
||||
protected boolean active;
|
||||
protected Config config;
|
||||
protected transient Config config;
|
||||
protected Logger logger;
|
||||
|
||||
public Processor(String id){
|
||||
@@ -63,15 +70,17 @@ public abstract class Processor {
|
||||
ss.leave(this);
|
||||
}
|
||||
}
|
||||
public void begin(Config conf){
|
||||
public void begin(Config conf) throws Exception{
|
||||
this.config=conf;
|
||||
};
|
||||
public void end(){
|
||||
}
|
||||
public void end() throws Exception{
|
||||
this.config=null;
|
||||
};
|
||||
}
|
||||
public void work() throws Exception{
|
||||
}
|
||||
protected Logger log(){
|
||||
// prefer local over central one
|
||||
Logger ret=logger!=null?logger:(config!=null?(Logger)config.getProperty("logger",null):null);
|
||||
Logger ret=logger!=null?logger:(config!=null?Config.LOGGER.get(config):null);
|
||||
// if none provided install a fresh one locally
|
||||
if(ret==null) ret=logger=LoggerFactory.getLogger(this.getId());
|
||||
return ret;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -51,7 +58,8 @@ public class Request {
|
||||
return http_request.getHeader(key);
|
||||
}
|
||||
public String getCookie(String name,String def){
|
||||
for(Cookie c:http_request.getCookies()){
|
||||
Cookie[] all=http_request.getCookies();
|
||||
if(all!=null) for(Cookie c:all){
|
||||
if(name.equalsIgnoreCase(c.getName())) return c.getValue();
|
||||
}
|
||||
return def;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -24,6 +31,8 @@ public class Response {
|
||||
public static final int HTTP_NOT_FOUND=HttpServletResponse.SC_NOT_FOUND;
|
||||
public static final int HTTP_UNAUTHORIZED=HttpServletResponse.SC_UNAUTHORIZED;
|
||||
public static final int HTTP_FORBIDDEN=HttpServletResponse.SC_FORBIDDEN;
|
||||
public static final int HTTP_TEMPORARY_REDIRECT=HttpServletResponse.SC_TEMPORARY_REDIRECT;
|
||||
public static final int HTTP_FOUND_REDIRECT=HttpServletResponse.SC_FOUND;
|
||||
|
||||
final protected HttpServletResponse http_response;
|
||||
final protected Writer char_response;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -67,7 +74,7 @@ public class ResponseEncoder {
|
||||
getOutputStream().write(buf,offset, len);
|
||||
return this;
|
||||
}
|
||||
public ResponseEncoder writeString(String str) throws IOException{
|
||||
public ResponseEncoder writeString(CharSequence str) throws IOException{
|
||||
getWriter().append(str);
|
||||
return this;
|
||||
}
|
||||
@@ -79,11 +86,11 @@ public class ResponseEncoder {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public ResponseEncoder writeln(String msg,Object ... args) throws IOException{
|
||||
public ResponseEncoder writeln(CharSequence msg,Object ... args) throws IOException{
|
||||
if(args.length==0){
|
||||
getWriter().append(msg).append("\n");
|
||||
}else{
|
||||
String str=MessageFormat.format(msg,args);
|
||||
String str=MessageFormat.format(msg.toString(),args);
|
||||
getWriter().append(str).append("\n");
|
||||
}
|
||||
return this;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Route {
|
||||
String path() default "{method}";
|
||||
String verb() default "GET|POST|DELETE";
|
||||
String return_mime() default "";
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/** Utility class that hosts and matches routes.
|
||||
* This object is needed so we can support parametrized paths.
|
||||
* It will match a route but also extract parameters.
|
||||
*/
|
||||
public class RouteDetector {
|
||||
String verb;
|
||||
String path;
|
||||
String pattern;
|
||||
final ArrayList<String> params=new ArrayList<String>();
|
||||
Pattern regex;
|
||||
Object payload;
|
||||
|
||||
public RouteDetector(String verb,String path){
|
||||
this.verb=verb;
|
||||
this.path=path;
|
||||
if(this.verb==null) this.verb="GET|POST|DELETE";
|
||||
verb=verb.toUpperCase();
|
||||
pattern=toPattern(verb, path);
|
||||
regex=Pattern.compile(pattern);
|
||||
Pattern p=Pattern.compile("\\{(.+)\\}");
|
||||
Matcher m=p.matcher(path);
|
||||
while(m.find()){
|
||||
String g=m.group();
|
||||
params.add(Handy.unwrap(g,"{","}"));
|
||||
}
|
||||
//if(params.isEmpty()==false) routeParams.put(routePat,params);
|
||||
}
|
||||
public String toString(){
|
||||
return getPattern();
|
||||
}
|
||||
public String getPattern(){
|
||||
return pattern;
|
||||
}
|
||||
public String getVerb(){
|
||||
return verb;
|
||||
}
|
||||
public String getPath(){
|
||||
return path;
|
||||
}
|
||||
public static String toPattern(String verb,String path){
|
||||
String pathPattern=path.replaceAll("\\{(.+)\\}","(.+)");
|
||||
String ret=Handy.wrap(verb,"(",")")+" "+pathPattern;
|
||||
if(!ret.endsWith("/") && !ret.endsWith("$")) ret+="$";
|
||||
return ret;
|
||||
}
|
||||
public boolean hasParams(){
|
||||
return !params.isEmpty();
|
||||
}
|
||||
public ArrayList<String> getParams(){
|
||||
return params;
|
||||
}
|
||||
public boolean matches(String pat){
|
||||
return matches(pat,null);
|
||||
}
|
||||
public boolean matches(String pat,Map<String,String> p){
|
||||
Matcher m=regex.matcher(pat);
|
||||
if(m.find()){ // do we match
|
||||
// we do - now possibly extract params
|
||||
if(p!=null){
|
||||
ArrayList<String> pms=getParams();
|
||||
for(int i=0;i<pms.size();i++){
|
||||
String val=m.group(1+i);
|
||||
String byName=pms.get(i).toLowerCase();
|
||||
p.put(byName,val);
|
||||
p.put("_arg"+i,val);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return getPattern().hashCode();
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if(o == null){
|
||||
return false;
|
||||
}
|
||||
if (o == this){
|
||||
return true;
|
||||
}
|
||||
if (o instanceof RouteDetector && getPattern().equals(((RouteDetector)o).getPattern())){
|
||||
return true;
|
||||
}
|
||||
if( o instanceof String) return matches((String)o,null);
|
||||
return false;
|
||||
}
|
||||
public RouteDetector setPayload(Object val){
|
||||
payload=val;
|
||||
return this;
|
||||
}
|
||||
public Object getPayload(){
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Routed {
|
||||
String path() default "{method}";
|
||||
String verb() default "GET|POST|DELETE";
|
||||
String return_mime() default "";
|
||||
}
|
||||
|
||||
+61
-39
@@ -1,24 +1,31 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
public class RouterEndPoint extends EndPoint{
|
||||
HashMap<String,EndPoint> routes=new HashMap<>();
|
||||
HashMap<String,ArrayList<String>> routeParams=new HashMap<>();
|
||||
ArrayList<String> patterns=new ArrayList<>(); // route patterns ordered
|
||||
public class RoutedEndPoint extends EndPoint{
|
||||
HashMap<String,EndPoint> routes=new HashMap<>(); // route pattern to endpoint
|
||||
ArrayList<RouteDetector> detectors=new ArrayList<>(); // route patterns ordered
|
||||
int[] indexes; // indexes for each route within regex
|
||||
Pattern regex;
|
||||
|
||||
public RouterEndPoint() {
|
||||
super(null);
|
||||
public RoutedEndPoint() {
|
||||
super("router");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,7 +39,6 @@ public class RouterEndPoint extends EndPoint{
|
||||
if(m!=null){
|
||||
//HashMap<String,String> pms=new HashMap<>();
|
||||
String rt=evalMatcher(m,req.getPathParams());
|
||||
//System.out.println(rt);
|
||||
//System.out.println(req.getPathParams());
|
||||
EndPoint ep=getRoute(rt);
|
||||
if(ep!=null){
|
||||
@@ -54,40 +60,30 @@ public class RouterEndPoint extends EndPoint{
|
||||
return routes.get(r);
|
||||
}
|
||||
public void addRoute(String verb,String path, EndPoint mm) {
|
||||
if(verb==null) verb="GET|POST|DELETE";
|
||||
String pathPat=path.replaceAll("\\{(.+)\\}","(.+)");
|
||||
String routePat=Handy.wrap(verb,"(",")")+" "+pathPat;
|
||||
if(!routePat.endsWith("/") && !routePat.endsWith("$")) routePat+="$";
|
||||
routes.put(routePat,mm);
|
||||
//System.out.println("Adding route:"+routePat);
|
||||
ArrayList<String> params=new ArrayList<String>();
|
||||
Pattern p=Pattern.compile("\\{(.+)\\}");
|
||||
Matcher m=p.matcher(path);
|
||||
while(m.find()){
|
||||
String g=m.group();
|
||||
params.add(Handy.unwrap(g,"{","}"));
|
||||
}
|
||||
if(params.isEmpty()==false) routeParams.put(routePat,params);
|
||||
RouteDetector det=new RouteDetector(verb,path);
|
||||
detectors.add(det);
|
||||
routes.put(det.getPattern(),mm);
|
||||
}
|
||||
|
||||
public void compile() {
|
||||
patterns.clear();
|
||||
for(String r:routes.keySet()){
|
||||
patterns.add(r);
|
||||
}
|
||||
// sort with longest first
|
||||
Collections.sort(patterns,Comparator.comparing((str)->{return -str.length();}));
|
||||
String fullPat = "("+String.join(")|(",patterns)+")";
|
||||
Collections.sort(detectors,Comparator.comparing((det)->{return -det.getPath().length();}));
|
||||
String fullPat=detectors
|
||||
.stream()
|
||||
.map(RouteDetector::toString)
|
||||
.collect(Collectors.joining(")|("));
|
||||
fullPat = "("+fullPat+")";
|
||||
//System.out.println("FUll:"+fullPat);
|
||||
regex=Pattern.compile(fullPat);
|
||||
// also recompute indexes
|
||||
indexes=new int[patterns.size()];
|
||||
indexes=new int[detectors.size()];
|
||||
int index=1;
|
||||
for (int i = 0; i < indexes.length; i++) {
|
||||
indexes[i]=index;
|
||||
String p=patterns.get(i);
|
||||
RouteDetector det=detectors.get(i);
|
||||
index+=2; // this includes the verb group
|
||||
if(routeParams.containsKey(p)){ // this includes any param groups
|
||||
index+=routeParams.get(p).size();
|
||||
if(det.hasParams()){ // this includes any param groups
|
||||
index+=det.getParams().size();
|
||||
}
|
||||
}
|
||||
//Arrays.stream(indexes).forEach(e->System.out.println(e+" "));
|
||||
@@ -102,8 +98,8 @@ public class RouterEndPoint extends EndPoint{
|
||||
/**
|
||||
* Find the route and return also url params.
|
||||
* url params are saved in two ways by name and by pos.
|
||||
* @param m
|
||||
* @param routeParams
|
||||
* @param m matcher to check
|
||||
* @param p parameters to reference
|
||||
* @return
|
||||
*/
|
||||
public String evalMatcher(Matcher m,Map<String,String> p){
|
||||
@@ -120,9 +116,10 @@ public class RouterEndPoint extends EndPoint{
|
||||
if(gindex==indexes[i]) rindex=i;
|
||||
}
|
||||
if(rindex<0) return null; // we can't match route to group
|
||||
String ret=patterns.get(rindex);
|
||||
if(p!=null && routeParams.containsKey(ret)){
|
||||
ArrayList<String> pms=routeParams.get(ret);
|
||||
RouteDetector det=detectors.get(rindex);
|
||||
//String ret=patterns.get(rindex);
|
||||
if(p!=null && det.hasParams()){
|
||||
ArrayList<String> pms=det.getParams();
|
||||
for(int i=0;i<pms.size();i++){
|
||||
String val=m.group(gindex+2+i);
|
||||
String byName=pms.get(i).toLowerCase();
|
||||
@@ -130,6 +127,31 @@ public class RouterEndPoint extends EndPoint{
|
||||
p.put("_arg"+i,val);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return det.getPattern();
|
||||
}
|
||||
/**
|
||||
* Will import endpoints to serve various paths.
|
||||
* We can call this multiple times for multiple targets.
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
public RoutedEndPoint importMethods(Object target){
|
||||
//RoutedEndPoint ret=new RoutedEndPoint();
|
||||
LinkedList<Method> routes=new LinkedList<>();
|
||||
Class<?> type=target.getClass();
|
||||
while (type != null) {
|
||||
for(Method m : type.getDeclaredMethods()){
|
||||
//System.out.println("Method:"+m.toString());
|
||||
if(m.getAnnotation(Routed.class)!=null){
|
||||
routes.add(0,m);
|
||||
}
|
||||
}
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
for(Method m:routes){
|
||||
MethodEndPoint mm=new MethodEndPoint(target,m);
|
||||
addRoute(mm.getVerb(),mm.getPath(),mm);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
import com.reliancy.jabbasec.SecurityPolicy;
|
||||
import com.reliancy.util.Template;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Router is entry point and servlet implementation that dispatches messages to our endpoints.
|
||||
* It will launch an embedded jetty server.
|
||||
* It will provide facilities to register endpoints.
|
||||
*/
|
||||
public class Router extends AbstractHandler{
|
||||
protected Connector[] connectors;
|
||||
protected Server jetty;
|
||||
protected Processor first=null;
|
||||
protected Processor last=null;
|
||||
protected RouterEndPoint main=null;
|
||||
protected transient Config config=null;
|
||||
protected Logger logger=LoggerFactory.getLogger(Router.class);
|
||||
|
||||
public Router() {
|
||||
jetty = new Server();
|
||||
jetty.setHandler(this);
|
||||
}
|
||||
public Config getConfig(){
|
||||
return config;
|
||||
}
|
||||
public void addProcessor(Processor m){
|
||||
if(first==null){
|
||||
last=first=m;
|
||||
}else{
|
||||
last.next=m;
|
||||
}
|
||||
while(last.next!=null) last=last.next;
|
||||
}
|
||||
public void removeProcessor(Processor m){
|
||||
if(first==m){
|
||||
if(first==last) last=null;
|
||||
first=first.next;
|
||||
while(last!=null && last.next!=null) last=last.next;
|
||||
}else{
|
||||
for(Processor prev=first;prev!=null;prev=prev.next){
|
||||
if(prev.next==m){
|
||||
if(last==m) last=prev;
|
||||
prev.next=m.next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m.next=null;
|
||||
}
|
||||
public Processor getProcessor(String id){
|
||||
for(Processor c=first;c!=null;c=c.next){
|
||||
if(c.getId().equalsIgnoreCase(id)) return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public RouterEndPoint getMain() {
|
||||
return main;
|
||||
}
|
||||
public void setMain(RouterEndPoint resolver) {
|
||||
this.main = resolver;
|
||||
}
|
||||
public RouterEndPoint importEndPoints(Object target){
|
||||
RouterEndPoint ret=new RouterEndPoint();
|
||||
LinkedList<Method> routes=new LinkedList<>();
|
||||
Class<?> type=target.getClass();
|
||||
while (type != null) {
|
||||
for(Method m : type.getDeclaredMethods()){
|
||||
//System.out.println("Method:"+m.toString());
|
||||
if(m.getAnnotation(Route.class)!=null){
|
||||
routes.add(0,m);
|
||||
}
|
||||
}
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
for(Method m:routes){
|
||||
//System.out.println("M:"+m);
|
||||
Route r=m.getAnnotation(Route.class);
|
||||
MethodEndPoint mm=new MethodEndPoint(target,m,r);
|
||||
ret.addRoute(r.verb(),mm.getPath(),mm);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void handle(String target,
|
||||
Request baseRequest,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
com.reliancy.jabba.Request req=new com.reliancy.jabba.Request(request);
|
||||
Response resp=new Response(response);
|
||||
CallSession ss=CallSession.getInstance();
|
||||
try{
|
||||
ss.begin(null, req, resp);
|
||||
if(first!=null) first.process(req, resp);
|
||||
if(main!=null) main.process(req,resp);
|
||||
}finally{
|
||||
ss.end();
|
||||
}
|
||||
}
|
||||
|
||||
public Connector[] getConnectors(){
|
||||
if(connectors!=null) return connectors;
|
||||
ServerConnector connector = new ServerConnector(jetty);
|
||||
connector.setReuseAddress(false);
|
||||
connector.setPort(8090);
|
||||
connectors=new Connector[] {connector};
|
||||
return connectors;
|
||||
}
|
||||
public void begin(Config conf) throws Exception{
|
||||
if(config!=null) throw new RuntimeException("Router running already");
|
||||
config=conf;
|
||||
for(Processor p=first;p!=null;p=p.getNext()){
|
||||
p.begin(config);
|
||||
}
|
||||
if(main!=null) main.begin(config);
|
||||
jetty.setConnectors(getConnectors());
|
||||
try{
|
||||
jetty.start();
|
||||
}catch(Exception ex){
|
||||
if(ex.getCause() instanceof java.net.BindException){
|
||||
logger.error("Bind issue",ex);
|
||||
Thread.sleep(3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void end() throws Exception{
|
||||
if(main!=null) main.end();
|
||||
for(Processor p=first;p!=null;p=p.getNext()){
|
||||
p.end();
|
||||
}
|
||||
config=null;
|
||||
logger.info("stopiing jetty");
|
||||
Connector[] connectors=jetty.getConnectors();
|
||||
System.out.println(connectors);
|
||||
if(connectors!=null) for(Connector c:connectors){
|
||||
ServerConnector cc=(ServerConnector) c;
|
||||
//System.out.println("stopping connecor:"+cc);
|
||||
try{
|
||||
cc.stop();
|
||||
cc.getConnectedEndPoints().forEach((endpoint)-> {
|
||||
//System.out.println("closing endpoint:"+endpoint);
|
||||
endpoint.close();
|
||||
});
|
||||
}finally{
|
||||
cc.close();
|
||||
//System.out.println("closing connecor:"+cc.getState());
|
||||
}
|
||||
}
|
||||
//System.out.println("signaling...");
|
||||
jetty.stop();
|
||||
//System.out.println("cleanup...");
|
||||
System.gc();
|
||||
//System.out.println("return...");
|
||||
}
|
||||
public void run(Config conf) throws Exception {
|
||||
try{
|
||||
begin(conf);
|
||||
//System.out.println("Entering server loop...");
|
||||
jetty.join();
|
||||
}finally{
|
||||
//System.out.println("Exiting server loop...");
|
||||
end();
|
||||
//System.out.println("Exiting server loop...done");
|
||||
}
|
||||
}
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
//System.out.println("Hello World!");
|
||||
Router app=new Router();
|
||||
app.addProcessor(new AppSessionFilter());
|
||||
app.addProcessor(new SecurityPolicy());
|
||||
app.setMain(app.importEndPoints(app));
|
||||
FileServer fs=new FileServer("/static","./var");
|
||||
fs.exportRoutes(app.getMain());
|
||||
app.run(null);
|
||||
//System.out.println("Goodbye World!");
|
||||
}
|
||||
|
||||
@Route()
|
||||
public String hello(){
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
context.put("name", "Jared");
|
||||
String ret="";
|
||||
try {
|
||||
Template.search_path("./var",SecurityPolicy.class);
|
||||
Template t=Template.find("/templates/login.hbs");
|
||||
System.out.println("Template:"+t);
|
||||
ret = t.render(context).toString();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
//#return "Hello World";
|
||||
}
|
||||
@Route(
|
||||
path="/helloPlain"
|
||||
)
|
||||
public void hello2(com.reliancy.jabba.Request req,Response resp) throws IOException{
|
||||
resp.getEncoder().writeln("Hi There");
|
||||
}
|
||||
@Route(
|
||||
path="/hello3/{idd:int}"
|
||||
)
|
||||
public String hello3(int id){
|
||||
return "Hello3:"+id;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
/** Session is temporary storage.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
|
||||
import com.reliancy.util.CodeException;
|
||||
import com.reliancy.util.ResultCode;
|
||||
|
||||
public class NeedCredentials extends CodeException {
|
||||
public static final int CODE=ResultCode.defineFailure(1,SecurityPolicy.class,"please provide credentials");
|
||||
|
||||
public NeedCredentials(){
|
||||
super(CODE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
|
||||
public class NotAuthentic extends RuntimeException {
|
||||
public NotAuthentic(String message){
|
||||
super(message);
|
||||
}
|
||||
public NotAuthentic(String message,Throwable cause){
|
||||
super(message,cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
/**
|
||||
* Our own exception to throw when we are not allowed access.
|
||||
*/
|
||||
public class NotPermitted extends RuntimeException{
|
||||
public NotPermitted(String message){
|
||||
super(message);
|
||||
}
|
||||
public NotPermitted(String message,Throwable cause){
|
||||
super(message,cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/**
|
||||
* Any entity that can be secured or permissions set for an actor.
|
||||
*/
|
||||
public interface Securable {
|
||||
public SecurityStore getStore();
|
||||
public Integer getId();
|
||||
default public Integer getOwnerId(){
|
||||
return getOwner().getId();
|
||||
}
|
||||
public String getKind();
|
||||
public String getName();
|
||||
default public String getTitle(){
|
||||
return Handy.prettyPrint(getName());
|
||||
}
|
||||
public String getIcon();
|
||||
public Securable getOwner();
|
||||
public List<Securable> getOwnedSecurables();
|
||||
public List<SecurityPermit> getDirectPermits();
|
||||
public SecurityPermit getPermit(Securable sec);
|
||||
public SecurityPolicy getPolicy();
|
||||
public default boolean isEssential(){
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
/** Annotation to indicate that this resource requruies authenticated actor and/or permit.
|
||||
* first of all we register this route with securitypolicy and enforce actor presence.
|
||||
* if user is not logged in we send to login form or use one of our protocols.
|
||||
* Additionally we can specify permits that are required.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Secured {
|
||||
String login_form() default "";
|
||||
String permits() default "";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
|
||||
/**
|
||||
* Interface that is implemented by any User or Principal entity.
|
||||
* Often an AppSession will be determined in many ways by the user.
|
||||
* SecurityActor is a very special type of Securable.
|
||||
*/
|
||||
public interface SecurityActor extends Securable{
|
||||
SecurityActor authPassword(String password);
|
||||
/** returns HA1 signature for Digest protocol. */
|
||||
String getDigestSignature(String realm);
|
||||
default boolean isRole(){
|
||||
return false;
|
||||
};
|
||||
}
|
||||
+11
-1
@@ -1,4 +1,11 @@
|
||||
package com.reliancy.jabbasec;
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
/**
|
||||
* An object describing what rights an actor has on a securable.
|
||||
* This object can be one individual rule or an effective merge of multiple rights.
|
||||
@@ -6,6 +13,8 @@ package com.reliancy.jabbasec;
|
||||
* We should start implementing from security policy and maybe we do not even need this class.
|
||||
*/
|
||||
public interface SecurityPermit {
|
||||
public SecurityStore getStore();
|
||||
public Integer getId();
|
||||
public SecurityActor getActor();
|
||||
public Securable getSubject();
|
||||
public boolean canRead();
|
||||
@@ -13,4 +22,5 @@ public interface SecurityPermit {
|
||||
public boolean canDelete();
|
||||
public boolean canCreate();
|
||||
public boolean canSecure();
|
||||
public boolean canExecute();
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.reliancy.jabba.AppSession;
|
||||
import com.reliancy.jabba.CallSession;
|
||||
import com.reliancy.jabba.Processor;
|
||||
import com.reliancy.jabba.Request;
|
||||
import com.reliancy.jabba.Response;
|
||||
import com.reliancy.jabba.RouteDetector;
|
||||
import com.reliancy.jabba.MethodDecorator;
|
||||
import com.reliancy.jabba.MethodEndPoint;
|
||||
import com.reliancy.util.CodeException;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/**
|
||||
* SecurityPolicy is a filter/processor that implements various auth protocols but also sources users.
|
||||
* The policy will produce new users once they are authenticated.
|
||||
* SecurityPolicy will authenticate SecurityActors and possibly authorize them via a permission mechanism
|
||||
* to access.
|
||||
* SecurityProtocol is one authenticatio method. We will not adjust response only try to recover user.
|
||||
* Initialization of auth will occur outside:
|
||||
* - for gui after login we will set an auth cookie to remember user and password
|
||||
* - for APIs if user is required will issue error 401 with WWW-Authenticate header
|
||||
*/
|
||||
public class SecurityPolicy extends Processor implements MethodDecorator.Factory{
|
||||
public static String REALM="reliancy";
|
||||
public static final String KEY_NAME="jbauth";
|
||||
protected String secret="sdfklgj 7150 9178-54=09";
|
||||
protected ArrayList<SecurityProtocol> protocols;
|
||||
protected SecurityActor admin;
|
||||
protected SecurityActor guest;
|
||||
protected final HashMap<Pattern,Secured> secured_pat=new HashMap<>(); // paths that require user
|
||||
protected SecurityStore store;
|
||||
|
||||
public SecurityPolicy() {
|
||||
super(SecurityPolicy.class.getSimpleName().toLowerCase());
|
||||
protocols=new ArrayList<>();
|
||||
protocols.add(new SecurityProtocol.Digest());
|
||||
protocols.add(new SecurityProtocol.Basic());
|
||||
}
|
||||
protected String getSecret(){
|
||||
return secret;
|
||||
}
|
||||
public SecurityPolicy setSecured(String path,Secured info){
|
||||
if(checkSecured(path)!=null) throw new IllegalStateException("Secured path cannot be secured again:"+path);
|
||||
Pattern regex=Pattern.compile(path);
|
||||
secured_pat.put(regex,info);
|
||||
return this;
|
||||
}
|
||||
public Secured checkSecured(String path){
|
||||
for(Pattern p:secured_pat.keySet()){
|
||||
if(p.pattern().equals(path)) return secured_pat.get(p);
|
||||
if(p.matcher(path).find()) return secured_pat.get(p);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public void before(Request request, Response response) throws IOException {
|
||||
// we will recover a user here
|
||||
CallSession css=CallSession.getInstance();
|
||||
AppSession ass=(AppSession) css.getAppSession();
|
||||
if(ass==null || ass.getUser()!=null){
|
||||
return; // we got a user all good
|
||||
}
|
||||
try{
|
||||
SecurityActor user=authenticate(request);
|
||||
if(user!=null) ass.setUser(user);
|
||||
}catch(NotAuthentic bad_cred){
|
||||
// we could not establish user
|
||||
response.setStatus(Response.HTTP_FORBIDDEN);
|
||||
response.getEncoder().writeObject(CodeException.getUserMessage(bad_cred));
|
||||
}catch(NeedCredentials no_cred){
|
||||
String login_form=no_cred.get("login_form");
|
||||
if(Handy.isBlank(login_form)){
|
||||
// we got no login form use HTTP auth
|
||||
response.setStatus(Response.HTTP_UNAUTHORIZED);
|
||||
String auth_supported=protocols.get(0).getSignature(REALM);
|
||||
//String auth_supported=protocols.stream().map(SecurityProtocol::getName).collect(Collectors.joining(","));
|
||||
response.setHeader("WWW-Authenticate",auth_supported);
|
||||
}else{
|
||||
// we got a login form do a redirect
|
||||
response.setStatus(Response.HTTP_FOUND_REDIRECT);
|
||||
String old_url=request.getPath();
|
||||
old_url=URLEncoder.encode(old_url,StandardCharsets.UTF_8.toString());
|
||||
//old_url=flask.escape(request.url.replace(request.url_root.strip("/"),""))
|
||||
//resp=flask.redirect("{}?next={}".format(login_pg,old_url),code=303)
|
||||
response.setHeader("Location",login_form+"?next="+old_url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void after(Request request, Response response) throws IOException {
|
||||
}
|
||||
@Override
|
||||
public void serve(Request request, Response response) throws IOException {
|
||||
// nothing to do here
|
||||
}
|
||||
/** authenticates or establishes user based on user and password.
|
||||
* same as loadActor but with first param being admin account.
|
||||
* @param name userid
|
||||
* @param pwd password
|
||||
* @return user we could establish
|
||||
* @throws NotPermitted
|
||||
* @throws IOException
|
||||
*/
|
||||
public SecurityActor authenticate(String name, String pwd) throws NotPermitted, IOException{
|
||||
return loadActor(admin, name, pwd);
|
||||
}
|
||||
/** authenticates or establishes user based on request and updates response.
|
||||
* this method might redirect to a login view. once it is done
|
||||
* @param req
|
||||
* @return user we could establish
|
||||
* @throws NotPermitted
|
||||
* @throws IOException
|
||||
*/
|
||||
public SecurityActor authenticate(Request req) throws IOException, NotAuthentic, NeedCredentials{
|
||||
// must recover user from cookies or by redirecting to login
|
||||
String verb=req.getVerb();
|
||||
String path=req.getPath();
|
||||
log().info("Path:"+path);
|
||||
String secpath=verb+" "+path;
|
||||
Secured secinfo=checkSecured(secpath);
|
||||
if(secinfo==null){
|
||||
return null; // this path is not secured
|
||||
}
|
||||
log().info("\tuser is needed, send back auth resp");
|
||||
String auth=req.getCookie(KEY_NAME,null);
|
||||
//log().info("Auth1:"+auth);
|
||||
if(auth!=null){
|
||||
// we have an auth cookie - encoded user login
|
||||
Map<String,String> kv=Handy.decrypt(getSecret(),auth);
|
||||
String username=(kv.get("n"));
|
||||
String password=(kv.get("p"));
|
||||
String address=(kv.get("a"));
|
||||
if(address!=null && !address.equals(req.getRemoteAddress())){
|
||||
return null; // invalid auth cookie
|
||||
}
|
||||
try {
|
||||
SecurityActor user = loadActor(admin,username,password);
|
||||
if(user!=null) return user;
|
||||
else throw new NotAuthentic("invalid credentials");
|
||||
} catch (NotPermitted e) {
|
||||
throw new NotAuthentic("not permitted to authenticate",e);
|
||||
}
|
||||
}
|
||||
// try authorization header as fallback - can't clear it always
|
||||
auth=req.getHeader("Authorization");
|
||||
//log().info("Auth2:"+auth);
|
||||
if(auth!=null){
|
||||
String[] kv=auth.split(" ",2);
|
||||
String proto_name=kv[0];
|
||||
String proto_args=kv.length>1?kv[1]:"";
|
||||
for(SecurityProtocol sproto:protocols){
|
||||
if(proto_name.equalsIgnoreCase(sproto.getName())){
|
||||
return sproto.authenticate(this, req,proto_args);
|
||||
}
|
||||
}
|
||||
throw new NotAuthentic("auth method not supported:"+proto_name);
|
||||
}
|
||||
throw new NeedCredentials().put("login_form",secinfo.login_form());
|
||||
//return null;
|
||||
}
|
||||
/** will establish what if any rights an actor has on a securable. */
|
||||
public SecurityPermit authorize(SecurityActor actor, Securable subject){
|
||||
return actor.getPermit(subject);
|
||||
}
|
||||
public SecurityPolicy setStore(SecurityStore store) throws NotPermitted, IOException{
|
||||
// this call will work unless store is locked already
|
||||
guest=(SecurityActor)store.loadSecurable(null,SecurityStore.GUEST);
|
||||
admin=(SecurityActor)store.loadSecurable(null,SecurityStore.ADMIN);
|
||||
if(guest==null) throw new IllegalArgumentException("store is missing guest actor");
|
||||
if(admin==null) throw new IllegalArgumentException("store is missing admin actor");
|
||||
// now we lock store
|
||||
store.setPolicy(this);
|
||||
this.store=store;
|
||||
return this;
|
||||
}
|
||||
public SecurityStore getStore(){
|
||||
return store;
|
||||
}
|
||||
/** will save a securable including a user if permitted via actor. */
|
||||
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException{
|
||||
store.saveSecurable(actor, sec);
|
||||
}
|
||||
/** loads a securable by id given actor permits. */
|
||||
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted{
|
||||
return store.loadSecurable(actor, id);
|
||||
}
|
||||
/** loads an actor given name and/or password.
|
||||
* if actor is an admin and no password is given it looks up actor by name.
|
||||
*/
|
||||
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted{
|
||||
return store.loadActor(actor, name, pwd);
|
||||
}
|
||||
/**
|
||||
* we do not create actual decorators we just register this method so we can intercept routes.
|
||||
*/
|
||||
@Override
|
||||
public MethodDecorator assertDecorator(MethodEndPoint mep, Annotation ann) {
|
||||
if(!(ann instanceof Secured)) return null;
|
||||
System.out.println("Assert decorator for:"+mep.getPath());
|
||||
String verb=mep.getVerb();
|
||||
String path=mep.getPath();
|
||||
String pat=RouteDetector.toPattern(verb, path);
|
||||
setSecured(pat,(Secured)ann);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.reliancy.jabba.Request;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/**
|
||||
* A SecurityProtocol will be processing HTTP to establish SecurityActor or user.
|
||||
*/
|
||||
public abstract class SecurityProtocol {
|
||||
protected final String name;
|
||||
public SecurityProtocol(String n){
|
||||
name=n;
|
||||
}
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
/// returns the signature to be send to client.
|
||||
public String getSignature(String realm){
|
||||
return getName()+String.format(" realm=\"%s\"",realm);
|
||||
}
|
||||
|
||||
public abstract SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException ;
|
||||
public static class Basic extends SecurityProtocol{
|
||||
public Basic(){
|
||||
super("Basic");
|
||||
}
|
||||
@Override
|
||||
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException {
|
||||
tok=String.valueOf(Handy.decodeBase64(tok)) ;
|
||||
String[] up=tok.split(":",2);
|
||||
String userid=up[0];
|
||||
String pwd=up.length>1?up[1]:"";
|
||||
return policy.loadActor(policy.admin,userid,pwd);
|
||||
}
|
||||
}
|
||||
public static class Digest extends SecurityProtocol{
|
||||
public Digest(){
|
||||
super("Digest");
|
||||
}
|
||||
@Override
|
||||
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException{
|
||||
Map<String,String> params=decode_params(tok);
|
||||
String username=params.get("username");
|
||||
String realm=params.get("realm");
|
||||
String nonce=params.get("nonce");
|
||||
//String uri=params.get("uri");
|
||||
String rsp_in=params.get("response");
|
||||
SecurityActor usr=policy.loadActor(policy.admin,username,null);
|
||||
//#print("USER:",usr)
|
||||
if(usr!=null){
|
||||
String ha1=usr.getDigestSignature(realm);
|
||||
String rsp_usr=digest_signature(ha1,nonce,req.getVerb(),req.getPath());
|
||||
//print("CHECK:",rsp_usr,rsp_in,uri,req.full_path)
|
||||
if(!rsp_usr.equals(rsp_in)){
|
||||
//this user response does not match one provided
|
||||
usr=null;
|
||||
}
|
||||
}
|
||||
if(usr==null) throw new NotAuthentic("Invalid credentials");
|
||||
return usr;
|
||||
}
|
||||
public String digest_signature(String ha1,String nonce,String method,String uri){
|
||||
//ha1_msg=user+":"+realm+":"+password
|
||||
//ha1=self.get_md5(ha1_msg)
|
||||
String ha2_msg=method+":"+uri;
|
||||
String ha2=Handy.hashMD5(ha2_msg);
|
||||
String rsp_msg=ha1+":"+nonce+":"+ha2;
|
||||
String rsp=Handy.hashMD5(rsp_msg);
|
||||
return rsp;
|
||||
}
|
||||
/** return a dict of all the values sent.*/
|
||||
public Map<String,String> decode_params(String tok){
|
||||
HashMap<String,String> ret=new HashMap<>();
|
||||
for(String kv:tok.trim().split(",")){
|
||||
String[] args=kv.trim().split("=",2);
|
||||
String k=Handy.trimRight(args[0]," \t\r\f\n");
|
||||
String v=args.length>1?Handy.trimEvenly(args[1],"'\""):"";
|
||||
ret.put(k,v);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/// returns a new nonce value.
|
||||
public String new_nonce(){
|
||||
//String nonce=util.get_md5(str(id(self)))
|
||||
String nonce=Handy.hashMD5(String.valueOf(hashCode()));
|
||||
return nonce;
|
||||
}
|
||||
@Override
|
||||
public String getSignature(String realm){
|
||||
//return getName()+String.format(" realm=\"%s\"",realm);
|
||||
String nonce=new_nonce();
|
||||
String ret=super.getSignature(realm);
|
||||
return String.format("%s, nonce=\"%s\"",ret,nonce);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Storage interface for security elemenets such as securables, actors and permits.
|
||||
* Storage is unlocked until a policy is set then it gets locked and it matters who
|
||||
* admin actor is.
|
||||
*
|
||||
* The lock is establish witht the install of a policy. The outside code including policy can
|
||||
* query any securable and especially admin account. Once the policy is set then admin account
|
||||
* must be used for privileged access.
|
||||
*/
|
||||
public interface SecurityStore {
|
||||
public static int GUEST=-1;
|
||||
public static int ADMIN=-2;
|
||||
|
||||
public Securable newSecurable();
|
||||
public SecurityActor newActor();
|
||||
public SecurityPermit newPermit();
|
||||
public void deleteSecurable(SecurityActor actor, Securable sec) throws IOException;
|
||||
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException;
|
||||
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted;
|
||||
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted;
|
||||
public List<Securable> loadSecurables(SecurityActor actor, Securable sec) throws IOException, NotPermitted;
|
||||
public void deletePermit(SecurityActor actor, SecurityPermit permit) throws IOException;
|
||||
public void savePermit(SecurityActor actor, SecurityPermit permit) throws IOException;
|
||||
public SecurityPermit loadPermit(SecurityActor actor, Integer id) throws IOException, NotPermitted;
|
||||
public List<SecurityPermit> loadPermitsBy(SecurityActor actor, SecurityActor sec) throws IOException, NotPermitted;
|
||||
public List<SecurityPermit> loadPermitsOn(SecurityActor actor, Securable sec) throws IOException, NotPermitted;
|
||||
public void setPolicy(SecurityPolicy policy);
|
||||
public SecurityPolicy getPolicy();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec.plain;
|
||||
|
||||
import com.reliancy.jabba.sec.SecurityActor;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
public class PlainActor extends PlainSecurable implements SecurityActor{
|
||||
String password;
|
||||
boolean role;
|
||||
|
||||
@Override
|
||||
public SecurityActor authPassword(String pwd) {
|
||||
if(password.equals(pwd)) return this;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
protected PlainActor setPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDigestSignature(String realm){
|
||||
String ha1_msg=this.getName()+":"+realm+":"+password;
|
||||
String ha1=Handy.hashMD5(ha1_msg);
|
||||
return ha1;
|
||||
}
|
||||
|
||||
public boolean isRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(boolean role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec.plain;
|
||||
|
||||
import com.reliancy.jabba.sec.Securable;
|
||||
import com.reliancy.jabba.sec.SecurityActor;
|
||||
import com.reliancy.jabba.sec.SecurityPermit;
|
||||
import com.reliancy.jabba.sec.SecurityStore;
|
||||
|
||||
public class PlainPermit implements SecurityPermit{
|
||||
SecurityStore store;
|
||||
Integer id;
|
||||
SecurityActor actor;
|
||||
Securable subject;
|
||||
boolean can_read;
|
||||
boolean can_write;
|
||||
boolean can_delete;
|
||||
boolean can_create;
|
||||
boolean can_secure;
|
||||
boolean can_execute;
|
||||
|
||||
@Override
|
||||
public SecurityStore getStore(){
|
||||
return store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityActor getActor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Securable getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return can_read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite() {
|
||||
return can_write;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDelete() {
|
||||
return can_delete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreate() {
|
||||
return can_create;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSecure() {
|
||||
return can_secure;
|
||||
}
|
||||
@Override
|
||||
public boolean canExecute() {
|
||||
return can_execute;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec.plain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.reliancy.jabba.sec.Securable;
|
||||
import com.reliancy.jabba.sec.SecurityPermit;
|
||||
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||
import com.reliancy.jabba.sec.SecurityStore;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
public class PlainSecurable implements Securable{
|
||||
SecurityStore store;
|
||||
Integer id;
|
||||
Securable owner;
|
||||
String kind;
|
||||
String name;
|
||||
String title;
|
||||
String icon;
|
||||
boolean essential;
|
||||
|
||||
@Override
|
||||
public SecurityStore getStore() {
|
||||
return store;
|
||||
}
|
||||
@Override
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
protected PlainSecurable setId(Integer id){
|
||||
this.id=id;
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
public PlainSecurable setKind(String v){
|
||||
kind=v;
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public PlainSecurable setName(String v){
|
||||
name=v;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title!=null?title:Handy.prettyPrint(getName());
|
||||
}
|
||||
public PlainSecurable setTitle(String v){
|
||||
title=v;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
public PlainSecurable setIcon(String v){
|
||||
icon=v;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Securable getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Securable> getOwnedSecurables() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SecurityPermit> getDirectPermits() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityPermit getPermit(Securable sec) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityPolicy getPolicy() {
|
||||
return store!=null?store.getPolicy():null;
|
||||
}
|
||||
public boolean isEssential() {
|
||||
return essential;
|
||||
}
|
||||
public PlainSecurable setEssential(boolean essential) {
|
||||
this.essential = essential;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.sec.plain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.reliancy.dbo.Bag;
|
||||
import com.reliancy.jabba.Path;
|
||||
import com.reliancy.jabba.sec.NotPermitted;
|
||||
import com.reliancy.jabba.sec.Securable;
|
||||
import com.reliancy.jabba.sec.SecurityActor;
|
||||
import com.reliancy.jabba.sec.SecurityPermit;
|
||||
import com.reliancy.jabba.sec.SecurityPolicy;
|
||||
import com.reliancy.jabba.sec.SecurityStore;
|
||||
|
||||
|
||||
/**
|
||||
* PlainSecurityStore is a container for plain security elements.
|
||||
* It will implement a simple in-memory list of items. Optionally it will
|
||||
* have be able to load or save its state to disk as json.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class PlainSecurityStore implements SecurityStore {
|
||||
final Bag<PlainSecurable> securables=new Bag<PlainSecurable>();
|
||||
final Bag<PlainPermit> permits=new Bag<PlainPermit>();
|
||||
SecurityPolicy policy;
|
||||
PlainActor guest;
|
||||
PlainActor admin;
|
||||
Path path;
|
||||
|
||||
public PlainSecurityStore() {
|
||||
guest=(PlainActor) newActor();
|
||||
guest.setPassword("").setName("guest").setEssential(true);
|
||||
admin=(PlainActor) newActor();
|
||||
admin.setPassword("admin").setName("admin").setTitle("Administrator").setEssential(true);
|
||||
securables.add(guest);
|
||||
securables.add(admin);
|
||||
}
|
||||
public PlainSecurityStore setPath(Path p){
|
||||
path=p;
|
||||
return this;
|
||||
}
|
||||
public void load() throws IOException{
|
||||
|
||||
}
|
||||
public void save() throws IOException{
|
||||
|
||||
}
|
||||
@Override
|
||||
public Securable newSecurable() {
|
||||
return new PlainSecurable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityActor newActor() {
|
||||
return new PlainActor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityPermit newPermit() {
|
||||
return new PlainPermit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSecurable(SecurityActor actor, Securable sec) throws IOException {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin && actor!=sec.getOwner()) throw new NotPermitted("admin or owner rights required");
|
||||
}
|
||||
securables.remove((PlainSecurable)sec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin && actor!=sec.getOwner()) throw new NotPermitted("admin or owner rights required");
|
||||
}
|
||||
securables.add((PlainSecurable)sec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||
}
|
||||
if(id==ADMIN) return admin;
|
||||
if(id==GUEST) return guest;
|
||||
for(Securable sec:securables){
|
||||
if(sec.getId()==id) return sec;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||
}
|
||||
for(Securable sec:securables){
|
||||
if(!(sec instanceof SecurityActor)) continue; // skip over non actors
|
||||
SecurityActor a=(SecurityActor) sec;
|
||||
if(!a.getName().equalsIgnoreCase(name)) continue; // name mismatch
|
||||
if(pwd!=null && a.authPassword(pwd)==a) return a; // match on password if provided
|
||||
boolean actor_permitted=actor==admin;
|
||||
if(!actor_permitted) continue; // actor is not permitted
|
||||
return a; // if permitted lookup by name role or user
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Securable> loadSecurables(SecurityActor actor, Securable sec) throws IOException, NotPermitted {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin && actor!=sec.getOwner()) throw new NotPermitted("admin or owner rights required");
|
||||
}
|
||||
ArrayList<Securable> ret=new ArrayList<>();
|
||||
for(Securable s:securables){
|
||||
if(sec==s.getOwner()) ret.add(s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePermit(SecurityActor actor, SecurityPermit permit) throws IOException {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin) throw new NotPermitted("admin or owner rights required");
|
||||
}
|
||||
permits.remove((PlainPermit)permit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePermit(SecurityActor actor, SecurityPermit permit) throws IOException {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin) throw new NotPermitted("admin or owner rights required");
|
||||
}
|
||||
permits.add((PlainPermit)permit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityPermit loadPermit(SecurityActor actor, Integer id) throws IOException, NotPermitted {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||
}
|
||||
for(SecurityPermit p:permits){
|
||||
if(p.getId()==id) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public List<SecurityPermit> loadPermitsBy(SecurityActor actor, SecurityActor sec) throws IOException, NotPermitted {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||
}
|
||||
ArrayList<SecurityPermit> ret=new ArrayList<>();
|
||||
for(SecurityPermit p:permits){
|
||||
if(p.getActor()==sec) ret.add(p);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@Override
|
||||
public List<SecurityPermit> loadPermitsOn(SecurityActor actor, Securable sec) throws IOException, NotPermitted {
|
||||
if(policy!=null){
|
||||
// if policy is set only admin level users can access actors via securable
|
||||
if(actor!=admin) throw new NotPermitted("admin rights required");
|
||||
}
|
||||
ArrayList<SecurityPermit> ret=new ArrayList<>();
|
||||
for(SecurityPermit p:permits){
|
||||
if(p.getSubject()==sec) ret.add(p);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPolicy(SecurityPolicy policy) {
|
||||
if(this.policy!=null) throw new IllegalStateException("Store is locked already.");
|
||||
this.policy=policy;
|
||||
}
|
||||
@Override
|
||||
public SecurityPolicy getPolicy() {
|
||||
return this.policy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.jabba.ui;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.reliancy.jabba.AppSession;
|
||||
|
||||
/**
|
||||
* List of Feedback events with siphon like iterator.
|
||||
* When iterating over the list it pops values as well.
|
||||
*/
|
||||
public class Feedback extends LinkedList<FeedbackLine>{
|
||||
public static Feedback get(){
|
||||
AppSession ass=AppSession.getInstance();
|
||||
return ass!=null?ass.getFeedback():null;
|
||||
}
|
||||
class Siphon implements Iterator<FeedbackLine>{
|
||||
final ListIterator<FeedbackLine> backend;
|
||||
public Siphon(ListIterator<FeedbackLine> it){
|
||||
backend=it;
|
||||
}
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return backend.hasNext();
|
||||
}
|
||||
@Override
|
||||
public FeedbackLine next() {
|
||||
FeedbackLine ret=backend.next();
|
||||
backend.remove();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Iterator<FeedbackLine> iterator(){
|
||||
return new Siphon(this.listIterator());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.ui;
|
||||
/**
|
||||
* Message object to inform users about things.
|
||||
*
|
||||
*/
|
||||
public class FeedbackLine {
|
||||
public static final String ERROR="danger";
|
||||
public static final String WARN="warning";
|
||||
public static final String INFO="info";
|
||||
String type;
|
||||
String message;
|
||||
public FeedbackLine(String typ,String message){
|
||||
this.type=typ;
|
||||
this.message=message;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
public static FeedbackLine error(String message){
|
||||
return new FeedbackLine(ERROR, message);
|
||||
}
|
||||
public static FeedbackLine warn(String message){
|
||||
return new FeedbackLine(WARN, message);
|
||||
}
|
||||
public static FeedbackLine info(String message){
|
||||
return new FeedbackLine(INFO, message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/** Helper or model class to manage menus and toolbars.
|
||||
*
|
||||
*/
|
||||
public class Menu extends MenuItem{
|
||||
public static final String TOP="/menu/top";
|
||||
public static final String LEFT="/menu/left";
|
||||
|
||||
public static final HashMap<String,Menu> menus=new HashMap<>();
|
||||
public static void publish(String name,Menu m){
|
||||
menus.put(name,m);
|
||||
}
|
||||
public static Menu request(String name){
|
||||
Menu ret=menus.get(name);
|
||||
if(ret==null){
|
||||
ret=new Menu(name);
|
||||
publish(name,ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
final ArrayList<MenuItem> items=new ArrayList<>();
|
||||
public Menu(String id) {
|
||||
super(id);
|
||||
}
|
||||
public int getSize(){
|
||||
return items.size();
|
||||
}
|
||||
public Menu add(MenuItem itm){
|
||||
items.add(itm);
|
||||
return this;
|
||||
}
|
||||
public Menu addSpacer(){
|
||||
return add(new MenuItem("###"));
|
||||
}
|
||||
public MenuItem find(String id){
|
||||
if(id.equalsIgnoreCase(getId())) return this;
|
||||
for(MenuItem itm:items) if(id.equalsIgnoreCase(itm.getId())) return itm;
|
||||
return null;
|
||||
}
|
||||
public List<MenuItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
public Iterable<MenuItem> getBefore() {
|
||||
final ArrayList<MenuItem> ret=new ArrayList<>();
|
||||
for(MenuItem itm:items){
|
||||
if("###".equals(itm.getId())) break;
|
||||
ret.add(itm);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public Iterable<MenuItem> getAfter() {
|
||||
final ArrayList<MenuItem> ret=new ArrayList<>();
|
||||
boolean adding=false;
|
||||
for(MenuItem itm:items){
|
||||
if("###".equals(itm.getId())){
|
||||
adding=true;
|
||||
}else if(adding){
|
||||
ret.add(itm);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.ui;
|
||||
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/** Individual menu items within a menu.
|
||||
*
|
||||
*/
|
||||
public class MenuItem {
|
||||
final String id;
|
||||
String title;
|
||||
String url;
|
||||
String icon;
|
||||
public MenuItem(String id) {
|
||||
this.id=id;
|
||||
this.title=Handy.prettyPrint(id);
|
||||
this.url="/"+id;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public int getSize(){
|
||||
return 0;
|
||||
}
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
public MenuItem setTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
public MenuItem setUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
public MenuItem setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.github.jknack.handlebars.Context;
|
||||
import com.reliancy.util.CodeException;
|
||||
|
||||
/**
|
||||
* Data context and render task for our handlebars UI system.
|
||||
* We create a rendering for a template, load it with data and then flush it at end into a document.
|
||||
* In this class we also inject some global variables such as menu, feedback and user.
|
||||
*/
|
||||
public class Rendering extends HashMap<String,Object>{
|
||||
public static Rendering begin(Template t){
|
||||
Rendering ret=new Rendering(t);
|
||||
ret.with("menu",Menu.request(Menu.TOP));
|
||||
ret.with("toolbar",Menu.request(Menu.LEFT));
|
||||
ret.with("feedback",Feedback.get());
|
||||
return ret;
|
||||
}
|
||||
public static Rendering begin(String path,Object ...sp){
|
||||
//if(sp==null ||sp.length==0) sp=new Object[]{"./var",App.class};
|
||||
Template t=Template.find(path,sp);
|
||||
if(t==null) throw new CodeException(Template.ERR_BADTEMPLATE).put("template",path);
|
||||
return begin(t);
|
||||
}
|
||||
Context ctx;
|
||||
Template template;
|
||||
public Rendering(Template t){
|
||||
ctx=Context.newBuilder(this).build();
|
||||
template=t;
|
||||
}
|
||||
public CharSequence end() throws IOException{
|
||||
try{
|
||||
CharSequence ret=template.render(ctx);
|
||||
return ret;
|
||||
}finally{
|
||||
ctx.destroy();
|
||||
}
|
||||
}
|
||||
public void end(Writer _out) throws IOException{
|
||||
try{
|
||||
template.render(ctx,_out);
|
||||
}finally{
|
||||
ctx.destroy();
|
||||
}
|
||||
}
|
||||
public Rendering with(String key,Object val){
|
||||
put(key,val);
|
||||
return this;
|
||||
}
|
||||
public Rendering with(Map<String,?> kv){
|
||||
for(Map.Entry<String,?> e:kv.entrySet()){
|
||||
put(e.getKey(),e.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Rendering with(Throwable ex){
|
||||
StringBuilder msg=new StringBuilder();
|
||||
StringBuilder title=new StringBuilder();
|
||||
CodeException.fillUserMessage(ex, msg, title);
|
||||
with("error_title",title.toString());
|
||||
with("error_message",msg);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
+34
-8
@@ -1,16 +1,27 @@
|
||||
package com.reliancy.util;
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba.ui;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.github.jknack.handlebars.Handlebars;
|
||||
import com.github.jknack.handlebars.helper.StringHelpers;
|
||||
import com.github.jknack.handlebars.io.AbstractTemplateLoader;
|
||||
import com.github.jknack.handlebars.io.TemplateSource;
|
||||
import com.github.jknack.handlebars.io.URLTemplateSource;
|
||||
import com.reliancy.util.Resources;
|
||||
import com.reliancy.util.ResultCode;
|
||||
|
||||
/*
|
||||
import com.hubspot.jinjava.Jinjava;
|
||||
@@ -49,17 +60,24 @@ public class Template {
|
||||
public TemplateSource sourceAt(String location) throws IOException {
|
||||
String fullpath=this.resolve(location);
|
||||
URL loc=Resources.findFirst(null,fullpath,Template.search_path);
|
||||
System.out.println(location+":"+loc+":"+fullpath);
|
||||
//System.out.println(location+":"+loc+":"+fullpath);
|
||||
if (loc == null) {
|
||||
Logger.getLogger(Template.class.getSimpleName()).warning("Missing template"+fullpath);
|
||||
throw new FileNotFoundException(location);
|
||||
}
|
||||
return new URLTemplateSource(location,loc);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
static Handlebars handlebars = new Handlebars(new HBLoader());
|
||||
static Handlebars handlebars;
|
||||
static{
|
||||
handlebars= new Handlebars(new HBLoader());
|
||||
StringHelpers.register(handlebars);
|
||||
/*
|
||||
for(ConditionalHelpers h:ConditionalHelpers.values()){
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static Object[] search_path;
|
||||
static HashMap<String,Template> cache=new HashMap<>();
|
||||
@@ -94,6 +112,7 @@ public class Template {
|
||||
if(sp!=null && sp.length>0) search_path=sp;
|
||||
return search_path;
|
||||
}
|
||||
public static final int ERR_BADTEMPLATE=ResultCode.defineFailure(0x01,Template.class,"bad template: ${template}");
|
||||
com.github.jknack.handlebars.Template recipe;
|
||||
final URL location;
|
||||
String source;
|
||||
@@ -114,13 +133,20 @@ public class Template {
|
||||
if(source==null) this.source=Resources.toString(location);
|
||||
return this;
|
||||
}
|
||||
public CharSequence render(Map<String,?> context) throws IOException{
|
||||
public CharSequence render(Object context) throws IOException{
|
||||
if(source==null) load();
|
||||
//String ret = jinjava.render(source, context);
|
||||
if(recipe==null){
|
||||
recipe=handlebars.compileInline(source);
|
||||
}
|
||||
String ret=recipe.apply(context);
|
||||
return ret;
|
||||
return recipe.apply(context);
|
||||
}
|
||||
public void render(Object context,Writer _out) throws IOException{
|
||||
if(source==null) load();
|
||||
//String ret = jinjava.render(source, context);
|
||||
if(recipe==null){
|
||||
recipe=handlebars.compileInline(source);
|
||||
}
|
||||
recipe.apply(context,_out);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.reliancy.jabbasec;
|
||||
|
||||
public class NotAuthentic extends RuntimeException {
|
||||
public NotAuthentic(String message){
|
||||
super(message);
|
||||
}
|
||||
public NotAuthentic(String message,Throwable cause){
|
||||
super(message,cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.reliancy.jabbasec;
|
||||
/**
|
||||
* Our own exception to throw when we are not allowed access.
|
||||
*/
|
||||
public class NotPermitted extends RuntimeException{
|
||||
public NotPermitted(String message){
|
||||
super(message);
|
||||
}
|
||||
public NotPermitted(String message,Throwable cause){
|
||||
super(message,cause);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.reliancy.jabbasec;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Any entity that can be secured or permissions set for an actor.
|
||||
*/
|
||||
public interface Securable {
|
||||
public Integer getId();
|
||||
public Integer getOwnerId();
|
||||
public String getKind();
|
||||
public String getName();
|
||||
public String getTitle();
|
||||
public String getIcon();
|
||||
public Securable getOwner();
|
||||
public List<Securable> getOwnedSecurables();
|
||||
public List<SecurityPermit> getDirectPermits();
|
||||
public SecurityPermit getPermit(Securable sec);
|
||||
public SecurityPolicy getPolicy();
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.reliancy.jabbasec;
|
||||
|
||||
/**
|
||||
* Interface that is implemented by any User or Principal entity.
|
||||
* Often an AppSession will be determined in many ways by the user.
|
||||
*/
|
||||
public interface SecurityActor extends Securable{
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package com.reliancy.jabbasec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import com.reliancy.jabba.AppSession;
|
||||
import com.reliancy.jabba.CallSession;
|
||||
import com.reliancy.jabba.Processor;
|
||||
import com.reliancy.jabba.Request;
|
||||
import com.reliancy.jabba.Response;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/**
|
||||
* SecurityPolicy is a filter/processor that implements various auth protocols but also sources users.
|
||||
* The policy will produce new users once they are authenticated.
|
||||
* SecurityPolicy will authenticate SecurityActors and possibly authorize them via a permission mechanism
|
||||
* to access.
|
||||
* SecurityProtocol is one authenticatio method. We will not adjust response only try to recover user.
|
||||
* Initialization of auth will occur outside:
|
||||
* - for gui after login we will set an auth cookie to remember user and password
|
||||
* - for APIs if user is required will issue error 401 with WWW-Authenticate header
|
||||
*/
|
||||
public class SecurityPolicy extends Processor{
|
||||
public static final String KEY_NAME="jbauth";
|
||||
protected String secret="sdfklgj 7150 9178-54=09";
|
||||
protected ArrayList<SecurityProtocol> protocols;
|
||||
protected SecurityActor admin;
|
||||
protected SecurityActor guest;
|
||||
|
||||
public SecurityPolicy() {
|
||||
super(SecurityPolicy.class.getSimpleName().toLowerCase());
|
||||
protocols=new ArrayList<>();
|
||||
protocols.add(new SecurityProtocol.Digest());
|
||||
protocols.add(new SecurityProtocol.Basic());
|
||||
}
|
||||
@Override
|
||||
public void before(Request request, Response response) throws IOException {
|
||||
// we will recover a user here
|
||||
CallSession css=CallSession.getInstance();
|
||||
AppSession ass=(AppSession) css.getAppSession();
|
||||
if(ass==null || ass.getUser()!=null){
|
||||
return; // we got a user all good
|
||||
}
|
||||
try{
|
||||
SecurityActor user=authenticate(request);
|
||||
if(user!=null) ass.setUser(user);
|
||||
}catch(NotAuthentic ex){
|
||||
// we could not establish user
|
||||
response.setStatus(Response.HTTP_FORBIDDEN);
|
||||
response.getEncoder().writeObject(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void after(Request request, Response response) throws IOException {
|
||||
}
|
||||
@Override
|
||||
public void serve(Request request, Response response) throws IOException {
|
||||
// nothing to do here
|
||||
}
|
||||
protected String getSecret(){
|
||||
return secret;
|
||||
}
|
||||
/** authenticates or establishes user based on request and updates response.
|
||||
* this method might redirect to a login view. once it is done
|
||||
* @param req
|
||||
* @param res
|
||||
* @return user we could establish
|
||||
* @throws NotPermitted
|
||||
* @throws IOException
|
||||
*/
|
||||
public SecurityActor authenticate(Request req) throws IOException, NotAuthentic{
|
||||
// must recover user from cookies or by redirecting to login
|
||||
log().info("User is missing.");
|
||||
String auth=req.getCookie(KEY_NAME,null);
|
||||
if(auth!=null){
|
||||
// we have an auth cookie - encoded user login
|
||||
Map<String,String> kv=Handy.decrypt(getSecret(),auth);
|
||||
String username=(kv.get("n"));
|
||||
String password=(kv.get("p"));
|
||||
String address=(kv.get("a"));
|
||||
if(address!=null && !address.equals(req.getRemoteAddress())){
|
||||
return null; // invalid auth cookie
|
||||
}
|
||||
try {
|
||||
SecurityActor user = loadActor(admin,username,password);
|
||||
if(user!=null) return user;
|
||||
else throw new NotAuthentic("invalid credentials");
|
||||
} catch (NotPermitted e) {
|
||||
throw new NotAuthentic("not permitted to authenticate",e);
|
||||
}
|
||||
}
|
||||
// try authorization header as fallback - can't clear it always
|
||||
auth=req.getHeader("Authorization");
|
||||
if(auth!=null){
|
||||
String[] kv=auth.split(" ",2);
|
||||
String proto_name=kv[0];
|
||||
String proto_args=kv.length>1?kv[1]:"";
|
||||
for(SecurityProtocol sproto:protocols){
|
||||
if(proto_name.equalsIgnoreCase(sproto.getName())){
|
||||
return sproto.authenticate(this, req,proto_args);
|
||||
}
|
||||
}
|
||||
throw new NotAuthentic("auth method not supported:"+proto_name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/** will establish what if any rights an actor has on a securable. */
|
||||
public SecurityPermit authorize(SecurityActor actor, Securable subject){
|
||||
return actor.getPermit(subject);
|
||||
}
|
||||
/** loads a securable by id given actor permits. */
|
||||
public Securable loadSecurable(SecurityActor actor, Integer id) throws IOException, NotPermitted{
|
||||
return null;
|
||||
}
|
||||
/** loads an actor given name and password. */
|
||||
public SecurityActor loadActor(SecurityActor actor, String name, String pwd) throws IOException, NotPermitted{
|
||||
return null;
|
||||
}
|
||||
/** will save a securable including a user if permitted via actor. */
|
||||
public void saveSecurable(SecurityActor actor, Securable sec) throws IOException{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.reliancy.jabbasec;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.reliancy.jabba.Request;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/**
|
||||
* A SecurityProtocol will be processing HTTP to establish SecurityActor or user.
|
||||
*/
|
||||
public abstract class SecurityProtocol {
|
||||
protected final String name;
|
||||
public SecurityProtocol(String n){
|
||||
name=n;
|
||||
}
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
public abstract SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException ;
|
||||
public static class Basic extends SecurityProtocol{
|
||||
public Basic(){
|
||||
super("Basic");
|
||||
}
|
||||
@Override
|
||||
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) throws NotPermitted, IOException {
|
||||
tok=String.valueOf(Handy.decodeBase64(tok)) ;
|
||||
String[] up=tok.split(":",2);
|
||||
String userid=up[0];
|
||||
String pwd=up.length>1?up[1]:"";
|
||||
return policy.loadActor(policy.admin,userid,pwd);
|
||||
}
|
||||
}
|
||||
public static class Digest extends SecurityProtocol{
|
||||
public Digest(){
|
||||
super("Digest");
|
||||
}
|
||||
@Override
|
||||
public SecurityActor authenticate(SecurityPolicy policy,Request req,String tok) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.rec;
|
||||
|
||||
/** Similar to a SAX interface used by parsers for XML and JSON to assemble DOM structures.
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.rec;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.rec;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.rec;
|
||||
|
||||
@@ -12,7 +14,7 @@ import com.reliancy.util.Tokenizer;
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/** Special class which will tokenize string according to rules for JSON and feed the info to a listener.
|
||||
*
|
||||
* TODO: reuse headers in an array if same structure
|
||||
* @author amer
|
||||
*/
|
||||
public class JSONDecoder implements TextDecoder,DecoderSink {
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.rec;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.rec;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.rec;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.rec;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.rec;
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.rec;
|
||||
/**
|
||||
* dimensioned container of values.
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.util;
|
||||
import java.util.HashMap;
|
||||
|
||||
/** One exception to rule them all.
|
||||
* This exception works with ResultCode and represents and instance with context information.
|
||||
* If a ResultCode is deemed parametric then we use provided parameters to update it when generating a message.
|
||||
*
|
||||
* @author amer
|
||||
*/
|
||||
public class CodeException extends RuntimeException {
|
||||
|
||||
protected final int code;
|
||||
protected final HashMap<String,Object> context=new HashMap<>();
|
||||
public CodeException(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
public CodeException(Throwable cause, int code) {
|
||||
super(cause);
|
||||
this.code = code;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return getMessage();
|
||||
}
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
public ResultCode getResultCode(){
|
||||
return ResultCode.get(code);
|
||||
}
|
||||
@Override
|
||||
public String getMessage() {
|
||||
ResultCode rcode=getResultCode();
|
||||
if(rcode!=null){
|
||||
boolean wrapped=(rcode.getCode()==ResultCode.FAILURE);
|
||||
String msg=rcode.getMessage();
|
||||
if(msg.contains("$")){
|
||||
for(String key:context.keySet()){
|
||||
Object obj=context.get(key);
|
||||
if(obj==null) continue;
|
||||
String val=String.valueOf(obj);
|
||||
msg=msg.replaceAll("\\$\\{"+key+"\\}",val);
|
||||
}
|
||||
}else if(this.getCause()!=null && wrapped){
|
||||
msg=CodeException.getUserMessage(this.getCause());
|
||||
}
|
||||
return msg;
|
||||
}else{
|
||||
return "("+String.format("%08X", code)+")";
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String name) {
|
||||
return (T)context.get(name);
|
||||
}
|
||||
|
||||
public CodeException put(String name, String value) {
|
||||
context.put(name, value);
|
||||
return this;
|
||||
}
|
||||
public static CodeException wrap(Throwable exception) {
|
||||
if (exception instanceof CodeException) {
|
||||
CodeException se = (CodeException)exception;
|
||||
return se;
|
||||
} else {
|
||||
return new CodeException(exception,ResultCode.FAILURE);
|
||||
}
|
||||
}
|
||||
public static String getUserMessage(Throwable ex,Object context) {
|
||||
return getUserMessage(ex);
|
||||
}
|
||||
public static String getUserMessage(Throwable ex) {
|
||||
StringBuilder buf=new StringBuilder();
|
||||
fillUserMessage(ex,buf,null);
|
||||
return buf.toString();
|
||||
}
|
||||
public static Throwable fillUserMessage(Throwable ex,StringBuilder msg,StringBuilder title) {
|
||||
Throwable c = ex;
|
||||
System.out.println(">>>"+c+"/"+c.getCause());
|
||||
while(c.getCause()!=null){
|
||||
Throwable cc= c.getCause();
|
||||
if(c.getMessage()==null){
|
||||
c=cc;continue;
|
||||
}
|
||||
String cMsg=c.getMessage();
|
||||
String ccMsg=cc.getMessage();
|
||||
System.out.println("!!!"+cMsg+"/"+c.getClass().getName()+"/"+cc.getClass().getName());
|
||||
boolean wrapped=(c instanceof CodeException) && ((CodeException)c).getCode()==ResultCode.FAILURE;
|
||||
boolean plain_at=cMsg.equals(c.getClass().getName());
|
||||
boolean plain_sub=cMsg.equals(cc.getClass().getName());
|
||||
boolean same_msg=cMsg.equalsIgnoreCase(ccMsg);
|
||||
System.out.println("\t"+plain_sub+"#"+cc+"$"+cc.getCause()+"*"+cc.getMessage());
|
||||
if(plain_at || plain_sub || cMsg.startsWith(cc.getClass().getName()+":") || same_msg || wrapped){
|
||||
c=cc;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
System.out.println("CC:"+c);
|
||||
// take care of title
|
||||
String _title=c.getClass().getSimpleName();
|
||||
if(c instanceof CodeException){
|
||||
CodeException cc=(CodeException) c;
|
||||
if(cc.getCause()!=null){
|
||||
_title=cc.getClass().getSimpleName();
|
||||
}else{
|
||||
// we do not have a cause
|
||||
int code=cc.getCode();
|
||||
ResultCode rcode=ResultCode.get(code);
|
||||
if(rcode!=null) _title=rcode.getSource();
|
||||
}
|
||||
}
|
||||
if(title!=null) title.append(_title);
|
||||
// now take care of detail
|
||||
String _msg=c.getLocalizedMessage();
|
||||
if(_msg==null || _msg.trim().isEmpty()){
|
||||
_msg=c.getClass().getSimpleName();
|
||||
StackTraceElement[] se=c.getStackTrace();
|
||||
if(se!=null && se.length>0) _msg+="\n\t at "+se[0].toString();
|
||||
}
|
||||
String prefString="Exception:";
|
||||
String prefString2="Error:";
|
||||
int prefix=_msg.lastIndexOf(prefString);
|
||||
if(prefix<0) prefix=_msg.lastIndexOf(prefString2);
|
||||
if(prefix>0 && _msg.substring(0, prefix).contains(".")) _msg=_msg.substring(prefix+prefString.length());
|
||||
if(msg!=null) msg.append(_msg);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.util;
|
||||
/**
|
||||
* Common utility methods.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -19,18 +23,56 @@ import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
/**
|
||||
* Common utility methods.
|
||||
*/
|
||||
public final class Handy {
|
||||
public static String wrap(String verb, String left, String right) {
|
||||
public static final String WHITE=" \t\r\f\n";
|
||||
/** place left-right around verb.*/
|
||||
public static String wrap(String verb, String left, String right) {
|
||||
if(verb==null) verb="";
|
||||
if(verb.startsWith(left) && verb.endsWith(right)) return verb;
|
||||
return left+verb.trim()+right;
|
||||
}
|
||||
/** remove left-right around verb.*/
|
||||
public static String unwrap(String verb, String left, String right) {
|
||||
if(verb==null) return verb;
|
||||
String ret=verb.trim();
|
||||
if(verb.startsWith(left) && verb.endsWith(right)) ret=verb.substring(1,verb.length()-1);
|
||||
return ret;
|
||||
}
|
||||
/** remove any chars elements from left of verb. */
|
||||
public static String trimLeft(String verb,String chars){
|
||||
while(verb.length()>0 && chars.indexOf(verb.charAt(0))!=-1){
|
||||
verb=verb.substring(1);
|
||||
}
|
||||
return verb;
|
||||
}
|
||||
/** remove any chars elements from right of verb. */
|
||||
public static String trimRight(String verb,String chars){
|
||||
while(verb.length()>0 && chars.indexOf(verb.charAt(verb.length()-1))!=-1){
|
||||
verb=verb.substring(0,verb.length()-1);
|
||||
}
|
||||
return verb;
|
||||
}
|
||||
/** remove any chars elements from right and right of verb. */
|
||||
public static String trimBoth(String verb,String chars){
|
||||
verb=trimLeft(verb, chars);
|
||||
verb=trimRight(verb, chars);
|
||||
return verb;
|
||||
}
|
||||
/** remove any chars elements from right and right of verb symetrically. trims whitespace first. */
|
||||
public static String trimEvenly(String verb,String chars){
|
||||
verb=trimBoth(verb," \t\n\r\f");
|
||||
while(verb.length()>1){
|
||||
char left=verb.charAt(0);
|
||||
char right=verb.charAt(verb.length()-1);
|
||||
if(left!=right) break; // left-right not even
|
||||
if(chars.indexOf(left)<0) break; // even but not in chars list
|
||||
verb=verb.substring(1,verb.length()-1);
|
||||
}
|
||||
return verb;
|
||||
}
|
||||
public static <T> T nz(T val, T def){
|
||||
return val!=null?val:def;
|
||||
}
|
||||
@@ -219,7 +261,7 @@ public final class Handy {
|
||||
return bufs.toString();
|
||||
}
|
||||
/** Attempts to take a user string and compact it to camel case.
|
||||
* @param str nicely formatted string
|
||||
* @param value more or less presentable string
|
||||
* @return nicely compact string
|
||||
*/
|
||||
public static String toCamelCase(String value) {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.util;
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -13,7 +17,7 @@ import java.net.URLDecoder;
|
||||
* It might but should not hold any handles. It holds the address and
|
||||
* possibly takes care of looking up addresses.
|
||||
* The richest syntax is:
|
||||
* PROTOCOL://USER:PWD@MACHINE:PORT/DATABASE?key=val&...
|
||||
* <pre> {@code PROTOCOL://USER:PWD@MACHINE:PORT/DATABASE?key=val&... } </pre>
|
||||
* Properties are held in their own string and need to be decoded.
|
||||
*
|
||||
* We use forward slash for path delimitation of database portion. For the rest we preserve other slashes to allow windows domain\\user or server\\instance.
|
||||
@@ -380,7 +384,7 @@ public class Path {
|
||||
* method will split paths used in linux and windows.
|
||||
* in particular for windows it checks if a single letter precedes a colon in which case it considers it a volume
|
||||
* and does not split there.
|
||||
* @param paths paths joined with colon or semi-colon
|
||||
* @param _paths paths joined with colon or semi-colon
|
||||
* @return array of paths
|
||||
*/
|
||||
public static String[] splitPaths(String _paths){
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/** Utility class to handle error codes and error messages.
|
||||
* Error codes are integers that describe outcome of an operation.
|
||||
*
|
||||
* In cases when return codes are used and not exceptions thrown it is a mess to keep track of what they mean.
|
||||
* With this class we define a uniform way of managing return codes and allow for additional information.
|
||||
* This additional information can be text that could be localized and give better user information about what happened.
|
||||
*
|
||||
* First we distinguish between success and failure. Any code that is negative is failure.
|
||||
* Default success code is 0 and provides no additional info. Any positive code is a warning or info and possibly carries extra meaning and
|
||||
* or description.
|
||||
*
|
||||
* success,failure,pending
|
||||
* source
|
||||
* parametric
|
||||
*/
|
||||
public class ResultCode {
|
||||
public static final byte TYPE_SUCCESS=0x0;
|
||||
public static final byte TYPE_PENDING=0x1;
|
||||
public static final byte TYPE_FAILURE=0xF;
|
||||
final int code;
|
||||
final String message;
|
||||
final String source;
|
||||
public ResultCode(byte type,short value,String src,String message) {
|
||||
this.message=message;
|
||||
this.source=src;
|
||||
this.code=ResultCode.getCode(type,value,source!=null?source.hashCode():0);
|
||||
}
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public byte getType() {
|
||||
return getType(code);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return getValue(code);
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
int code=getCode();
|
||||
String context=getSource();
|
||||
String message=getMessage();
|
||||
if(context!=null){
|
||||
return context+"("+String.format("%08X", code)+"):"+message;
|
||||
}else{
|
||||
return "("+String.format("%08X", code)+"):"+message;
|
||||
}
|
||||
}
|
||||
protected static final HashMap<Integer,ResultCode> codes=new HashMap<>();
|
||||
public static final int getCode(byte type,int value,int source){
|
||||
int st=(type <<28) & 0xF0000000;
|
||||
int sc=(source <<8) & 0x0FFFFF00;
|
||||
int vl=(value) & 0x000000FF;
|
||||
return (int) (st | sc | vl);
|
||||
}
|
||||
public static final byte getType(int code){
|
||||
return (byte)((code>>28) & 0x0F);
|
||||
}
|
||||
public static final boolean testType(int code,byte st){
|
||||
return getType(code)==st;
|
||||
}
|
||||
public static final boolean isSuccess(int code){
|
||||
return testType(code,TYPE_SUCCESS);
|
||||
}
|
||||
public static final boolean isFailure(int code,int st){
|
||||
return testType(code,TYPE_FAILURE);
|
||||
}
|
||||
public static final boolean isPending(int code,int st){
|
||||
return testType(code,TYPE_PENDING);
|
||||
}
|
||||
public static final int getValue(int code){
|
||||
return (int)(code & 0x000000FF);
|
||||
}
|
||||
public static final int getSource(int code){
|
||||
return (int)((code & 0x0FFFFF00)>>8);
|
||||
}
|
||||
public static final synchronized ResultCode get(int code){
|
||||
if(codes==null) return null;
|
||||
return (ResultCode)codes.get(code);
|
||||
}
|
||||
public static final synchronized ResultCode put(ResultCode c){
|
||||
ResultCode old=(ResultCode) codes.get(c.getCode());
|
||||
codes.put(c.getCode(),c);
|
||||
return old;
|
||||
}
|
||||
public static final int define(byte type,int value,Class<?> source,String message){
|
||||
return define(type,value,source!=null?source.getSimpleName():null,message);
|
||||
}
|
||||
public static final int define(byte type,int value,String source,String message){
|
||||
int code=getCode(type,value,source!=null?source.hashCode():0);
|
||||
ResultCode c=get(code);
|
||||
if(c!=null){
|
||||
System.err.println("Result code redefinition(consider different value or source):"+c);
|
||||
return code;
|
||||
}
|
||||
c=new ResultCode(type, (short) value,source,message);
|
||||
put(c);
|
||||
return code;
|
||||
}
|
||||
public static final int defineSuccess(int value,Class<?> source,String message){
|
||||
return define(TYPE_SUCCESS,value,source,message);
|
||||
}
|
||||
public static final int defineFailure(int value,Class<?> source,String message){
|
||||
return define(TYPE_FAILURE,value,source,message);
|
||||
}
|
||||
public static final int definePending(int value,Class<?> source,String message){
|
||||
return define(TYPE_PENDING,value,source,message);
|
||||
}
|
||||
public static final int SUCCESS=ResultCode.defineSuccess(0,null,"Success");
|
||||
public static final int FAILURE=ResultCode.defineFailure(0,null,"Failure");
|
||||
public static final int PENDING=ResultCode.definePending(0,null,"Pending");
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
package com.reliancy.util;
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{{#partial "MainSection"}}
|
||||
<div class="row justify-content-center mt-3">
|
||||
<div class="card">
|
||||
<div class="card-header alert alert-danger">
|
||||
<h5 class="card-title"><i class="fas fa-exclamation-circle fa-2x"></i> {{error_title}}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!--<h5 class="card-title">Special title treatment</h5>-->
|
||||
<p class="card-text">
|
||||
{{error_message}}
|
||||
</p>
|
||||
<!--<a href="#" class="btn btn-primary">Go somewhere</a>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{/partial}}
|
||||
{{> frame-1c}}
|
||||
@@ -1,4 +1,24 @@
|
||||
{{#partial "content"}}
|
||||
Calling base
|
||||
{{#partial "MainSection"}}
|
||||
<div class="container col-fixed">
|
||||
<form class="form-signin" method="POST">
|
||||
<!--
|
||||
<img class="mb-4" src="https://getbootstrap.com/docs/4.0/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72">
|
||||
-->
|
||||
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
|
||||
<label for="userid" class="sr-only">Email address</label>
|
||||
<input id="userid" name="userid" class="form-control" placeholder="Email address" required autofocus>
|
||||
<label for="password" class="sr-only">Password</label>
|
||||
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
|
||||
<div class="checkbox mb-3">
|
||||
<!--
|
||||
<label>
|
||||
<input type="checkbox" name="" value="remember-me"> Remember me
|
||||
</label>
|
||||
-->
|
||||
</div>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
|
||||
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
|
||||
</form>
|
||||
</div>
|
||||
{{/partial}}
|
||||
{{> base}}
|
||||
{{> frame-1c}}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.dbo;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
import com.reliancy.rec.JSON;
|
||||
@@ -93,25 +99,38 @@ public class TerminalTest {
|
||||
@Test
|
||||
public void complexCRUD() throws IOException, SQLException{
|
||||
System.out.println("ComplexCRUD");
|
||||
// Reading
|
||||
try(Action act=t.begin().load(Product.class).execute()){
|
||||
for(DBO o:act){
|
||||
System.out.println("DBO:"+o);
|
||||
}
|
||||
}
|
||||
//Saving
|
||||
Product p=new Product();
|
||||
p.setStatus(DBO.Status.USED);
|
||||
Product.id.set(p,35);
|
||||
Product.kind.set(p,Product.class.getSimpleName());
|
||||
Product.name.set(p,"myproduct");
|
||||
Product.created.set(p,new Date());
|
||||
Product.short_info.set(p,"a sweet melody");
|
||||
Product.short_info.set(p,"a sweet melody:"+new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
Product.display_name.set(p,"first entry");
|
||||
System.out.println("P0:"+JSON.toString(p));
|
||||
System.out.println("Update P0:"+JSON.toString(p));
|
||||
t.save(p);
|
||||
System.out.println("P1:"+JSON.toString(p));
|
||||
Product pp=t.load(Product.class, 35);
|
||||
System.out.println("Update P1:"+JSON.toString(p));
|
||||
// Creating
|
||||
Product pp=new Product();
|
||||
Product.kind.set(pp,Product.class.getSimpleName());
|
||||
Product.name.set(pp,"myproduct");
|
||||
Product.created.set(pp,new Date());
|
||||
Product.short_info.set(pp,"a sweet melody:");
|
||||
Product.display_name.set(pp,"created entry:"+new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
t.save(pp);
|
||||
System.out.println("Create PP0:"+JSON.toString(pp));
|
||||
pp=t.load(Product.class, Product.id.get(pp,null));
|
||||
System.out.println("Returning:"+pp);
|
||||
//t.delete(pp);
|
||||
Entity.retract(Maps.class);
|
||||
// Deleting
|
||||
t.delete(pp);
|
||||
//Entity.retract(Maps.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
package com.reliancy.jabba;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -31,8 +38,9 @@ public class RouterTest
|
||||
{
|
||||
//assertTrue( true );
|
||||
System.out.println("Test router init...");
|
||||
Router r=new Router();
|
||||
RouterEndPoint rep=r.importEndPoints(r);
|
||||
JettyApp r=new JettyApp();
|
||||
RoutedEndPoint rep=new RoutedEndPoint();
|
||||
rep.importMethods(r);
|
||||
rep.compile();
|
||||
//Matcher m=rep.match("GET","/helloPlain");
|
||||
Matcher m=rep.match("GET","/hello3/45");
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.reliancy.rec;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
/*
|
||||
Copyright (c) 2011-2022 Reliancy LLC
|
||||
|
||||
Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
|
||||
You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
You may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
package com.reliancy.rec;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user