From 43854a9e3310ff7a92257d16c4fc0a8321eaec68 Mon Sep 17 00:00:00 2001 From: sg481n Date: Thu, 3 Aug 2017 17:27:34 -0400 Subject:  [AAF-21] Initial code import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I63d7d499bbd46f500b5f5a4db966166f613f327a Signed-off-by: sg481n --- .../src/main/java/com/att/authz/common/Define.java | 51 +++ .../src/main/java/com/att/authz/env/AuthzEnv.java | 265 +++++++++++ .../main/java/com/att/authz/env/AuthzTrans.java | 71 +++ .../java/com/att/authz/env/AuthzTransFilter.java | 165 +++++++ .../java/com/att/authz/env/AuthzTransImpl.java | 198 +++++++++ .../com/att/authz/env/AuthzTransOnlyFilter.java | 89 ++++ .../src/main/java/com/att/authz/env/NullTrans.java | 225 ++++++++++ .../main/java/com/att/authz/layer/FacadeImpl.java | 39 ++ .../src/main/java/com/att/authz/layer/Result.java | 326 ++++++++++++++ .../src/main/java/com/att/authz/local/AbsData.java | 215 +++++++++ .../main/java/com/att/authz/local/DataFile.java | 186 ++++++++ .../main/java/com/att/authz/local/TextIndex.java | 253 +++++++++++ .../main/java/com/att/authz/org/EmailWarnings.java | 35 ++ .../src/main/java/com/att/authz/org/Executor.java | 36 ++ .../main/java/com/att/authz/org/Organization.java | 491 +++++++++++++++++++++ .../com/att/authz/org/OrganizationException.java | 54 +++ .../com/att/authz/org/OrganizationFactory.java | 148 +++++++ .../main/java/com/att/authz/server/AbsServer.java | 150 +++++++ authz-core/src/main/java/com/att/cache/Cache.java | 196 ++++++++ .../src/main/java/com/att/cssa/rserv/Acceptor.java | 170 +++++++ .../java/com/att/cssa/rserv/CachingFileAccess.java | 477 ++++++++++++++++++++ .../main/java/com/att/cssa/rserv/CodeSetter.java | 54 +++ .../src/main/java/com/att/cssa/rserv/Content.java | 116 +++++ .../src/main/java/com/att/cssa/rserv/HttpCode.java | 112 +++++ .../main/java/com/att/cssa/rserv/HttpMethods.java | 31 ++ .../src/main/java/com/att/cssa/rserv/Match.java | 212 +++++++++ .../src/main/java/com/att/cssa/rserv/Pair.java | 44 ++ .../src/main/java/com/att/cssa/rserv/RServlet.java | 156 +++++++ .../src/main/java/com/att/cssa/rserv/Route.java | 143 ++++++ .../main/java/com/att/cssa/rserv/RouteReport.java | 35 ++ .../src/main/java/com/att/cssa/rserv/Routes.java | 91 ++++ .../main/java/com/att/cssa/rserv/TransFilter.java | 137 ++++++ .../java/com/att/cssa/rserv/TransOnlyFilter.java | 78 ++++ .../main/java/com/att/cssa/rserv/TypedCode.java | 269 +++++++++++ .../src/main/java/com/att/cssa/rserv/Version.java | 94 ++++ .../main/java/com/att/cssa/rserv/doc/ApiDoc.java | 43 ++ 36 files changed, 5455 insertions(+) create mode 100644 authz-core/src/main/java/com/att/authz/common/Define.java create mode 100644 authz-core/src/main/java/com/att/authz/env/AuthzEnv.java create mode 100644 authz-core/src/main/java/com/att/authz/env/AuthzTrans.java create mode 100644 authz-core/src/main/java/com/att/authz/env/AuthzTransFilter.java create mode 100644 authz-core/src/main/java/com/att/authz/env/AuthzTransImpl.java create mode 100644 authz-core/src/main/java/com/att/authz/env/AuthzTransOnlyFilter.java create mode 100644 authz-core/src/main/java/com/att/authz/env/NullTrans.java create mode 100644 authz-core/src/main/java/com/att/authz/layer/FacadeImpl.java create mode 100644 authz-core/src/main/java/com/att/authz/layer/Result.java create mode 100644 authz-core/src/main/java/com/att/authz/local/AbsData.java create mode 100644 authz-core/src/main/java/com/att/authz/local/DataFile.java create mode 100644 authz-core/src/main/java/com/att/authz/local/TextIndex.java create mode 100644 authz-core/src/main/java/com/att/authz/org/EmailWarnings.java create mode 100644 authz-core/src/main/java/com/att/authz/org/Executor.java create mode 100644 authz-core/src/main/java/com/att/authz/org/Organization.java create mode 100644 authz-core/src/main/java/com/att/authz/org/OrganizationException.java create mode 100644 authz-core/src/main/java/com/att/authz/org/OrganizationFactory.java create mode 100644 authz-core/src/main/java/com/att/authz/server/AbsServer.java create mode 100644 authz-core/src/main/java/com/att/cache/Cache.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/Content.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/Match.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/Pair.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/RServlet.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/Route.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/Routes.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/Version.java create mode 100644 authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java (limited to 'authz-core/src/main/java/com') diff --git a/authz-core/src/main/java/com/att/authz/common/Define.java b/authz-core/src/main/java/com/att/authz/common/Define.java new file mode 100644 index 00000000..747ab508 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/common/Define.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.common; + +import com.att.cadi.CadiException; +import com.att.cadi.config.Config; +import com.att.inno.env.Env; + +public class Define { + public static String ROOT_NS="NS.Not.Set"; + public static String ROOT_COMPANY=ROOT_NS; + + public static void set(Env env) throws CadiException { + ROOT_NS = env.getProperty(Config.AAF_ROOT_NS); + if(ROOT_NS==null) { + throw new CadiException(Config.AAF_ROOT_NS + " property is required."); + } + ROOT_COMPANY = env.getProperty(Config.AAF_ROOT_COMPANY); + if(ROOT_COMPANY==null) { + int last = ROOT_NS.lastIndexOf('.'); + if(last>=0) { + ROOT_COMPANY = ROOT_NS.substring(0, last); + } else { + throw new CadiException(Config.AAF_ROOT_COMPANY + " or " + Config.AAF_ROOT_NS + " property with 3 positions is required."); + } + } + env.init().log("AAF Root NS is " + ROOT_NS + ", and AAF Root Company is " +ROOT_COMPANY); + } + +} diff --git a/authz-core/src/main/java/com/att/authz/env/AuthzEnv.java b/authz-core/src/main/java/com/att/authz/env/AuthzEnv.java new file mode 100644 index 00000000..b5015b0c --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/env/AuthzEnv.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.env; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map.Entry; +import java.util.Properties; + +import com.att.cadi.Access; +import com.att.cadi.Symm; +import com.att.cadi.config.Config; +import com.att.inno.env.APIException; +import com.att.inno.env.Decryptor; +import com.att.inno.env.Encryptor; +import com.att.inno.env.impl.Log4JLogTarget; +import com.att.inno.env.log4j.LogFileNamer; +import com.att.rosetta.env.RosettaEnv; + + +/** + * AuthzEnv is the Env tailored to Authz Service + * + * Most of it is derived from RosettaEnv, but it also implements Access, which + * is an Interface that Allows CADI to interact with Container Logging + * + * + */ +public class AuthzEnv extends RosettaEnv implements Access { + private long[] times = new long[20]; + private int idx = 0; + //private int mask = Level.AUDIT.maskOf(); + + public AuthzEnv() { + super(); + } + + public AuthzEnv(String ... args) { + super(args); + } + + public AuthzEnv(Properties props) { + super(Config.CADI_PROP_FILES,props); + } + + + @Override + public AuthzTransImpl newTrans() { + synchronized(this) { + times[idx]=System.currentTimeMillis(); + if(++idx>=times.length)idx=0; + } + return new AuthzTransImpl(this); + } + + /** + * Create a Trans, but do not include in Weighted Average + * @return + */ + public AuthzTrans newTransNoAvg() { + return new AuthzTransImpl(this); + } + + public long transRate() { + int count = 0; + long pot = 0; + long prev = 0; + for(int i=idx;i0) { + if(prev>0) { + ++count; + pot += times[i]-prev; + } + prev = times[i]; + } + } + for(int i=0;i0) { + if(prev>0) { + ++count; + pot += times[i]-prev; + } + prev = times[i]; + } + } + + return count==0?300000L:pot/count; // Return Weighted Avg, or 5 mins, if none avail. + } + + @Override + public ClassLoader classLoader() { + return getClass().getClassLoader(); + } + + @Override + public void load(InputStream is) throws IOException { + Properties props = new Properties(); + props.load(is); + for(Entry es : props.entrySet()) { + String key = es.getKey().toString(); + String value =es.getValue().toString(); + put(staticSlot(key==null?null:key.trim()),value==null?null:value.trim()); + } + } + + @Override + public void log(Level lvl, Object... msgs) { +// if(lvl.inMask(mask)) { +// switch(lvl) { +// case INIT: +// init().log(msgs); +// break; +// case AUDIT: +// audit().log(msgs); +// break; +// case DEBUG: +// debug().log(msgs); +// break; +// case ERROR: +// error().log(msgs); +// break; +// case INFO: +// info().log(msgs); +// break; +// case WARN: +// warn().log(msgs); +// break; +// case NONE: +// break; +// } +// } + } + + @Override + public void log(Exception e, Object... msgs) { + error().log(e,msgs); + } + + //@Override + public void printf(Level level, String fmt, Object... elements) { + if(willLog(level)) { + log(level,String.format(fmt, elements)); + } + } + + /* (non-Javadoc) + * @see com.att.cadi.Access#willLog(com.att.cadi.Access.Level) + */ + @Override + public boolean willLog(Level level) { + +// if(level.inMask(mask)) { +// switch(level) { +// case INIT: +// return init().isLoggable(); +// case AUDIT: +// return audit().isLoggable(); +// case DEBUG: +// return debug().isLoggable(); +// case ERROR: +// return error().isLoggable(); +// case INFO: +// return info().isLoggable(); +// case WARN: +// return warn().isLoggable(); +// case NONE: +// return false; +// } +// } + return false; + } + + @Override + public void setLogLevel(Level level) { + super.debug().isLoggable(); + //level.toggle(mask); + } + + public void setLog4JNames(String path, String root, String _service, String _audit, String _init, String _trace) throws APIException { + LogFileNamer lfn = new LogFileNamer(root); + if(_service==null) { + throw new APIException("AuthzEnv.setLog4JNames \"_service\" required (as default). Others can be null"); + } + String service=_service=lfn.setAppender(_service); // when name is split, i.e. authz|service, the Appender is "authz", and "service" + String audit=_audit==null?service:lfn.setAppender(_audit); // is part of the log-file name + String init=_init==null?service:lfn.setAppender(_init); + String trace=_trace==null?service:lfn.setAppender(_trace); + //TODO Validate path on Classpath + lfn.configure(path); + super.fatal = new Log4JLogTarget(service,org.apache.log4j.Level.FATAL); + super.error = new Log4JLogTarget(service,org.apache.log4j.Level.ERROR); + super.warn = new Log4JLogTarget(service,org.apache.log4j.Level.WARN); + super.audit = new Log4JLogTarget(audit,org.apache.log4j.Level.WARN); + super.init = new Log4JLogTarget(init,org.apache.log4j.Level.WARN); + super.info = new Log4JLogTarget(service,org.apache.log4j.Level.INFO); + super.debug = new Log4JLogTarget(service,org.apache.log4j.Level.DEBUG); + super.trace = new Log4JLogTarget(trace,org.apache.log4j.Level.TRACE); + } + + private static final byte[] ENC="enc:???".getBytes(); + public String decrypt(String encrypted, final boolean anytext) throws IOException { + if(encrypted==null) { + throw new IOException("Password to be decrypted is null"); + } + if(anytext || encrypted.startsWith("enc:")) { + if(decryptor.equals(Decryptor.NULL) && getProperty(Config.CADI_KEYFILE)!=null) { + final Symm s = Symm.obtain(this); + decryptor = new Decryptor() { + private Symm symm = s; + @Override + public String decrypt(String encrypted) { + try { + return (encrypted!=null && (anytext || encrypted.startsWith(Symm.ENC))) + ? symm.depass(encrypted) + : encrypted; + } catch (IOException e) { + return ""; + } + } + }; + encryptor = new Encryptor() { + @Override + public String encrypt(String data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + baos.write(ENC); + return "enc:???"+s.enpass(data); + } catch (IOException e) { + return ""; + } + } + + }; + } + return decryptor.decrypt(encrypted); + } else { + return encrypted; + } + } + + + +} diff --git a/authz-core/src/main/java/com/att/authz/env/AuthzTrans.java b/authz-core/src/main/java/com/att/authz/env/AuthzTrans.java new file mode 100644 index 00000000..129ddddc --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/env/AuthzTrans.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.env; + +import java.security.Principal; + +import javax.servlet.http.HttpServletRequest; + +import com.att.authz.org.Organization; +import com.att.cadi.Lur; +import com.att.cadi.Permission; +import com.att.inno.env.LogTarget; +import com.att.inno.env.TransStore; + +public interface AuthzTrans extends TransStore { + public abstract AuthzTrans set(HttpServletRequest req); + + public abstract void setUser(Principal p); + + public abstract String user(); + + public abstract Principal getUserPrincipal(); + + public abstract String ip(); + + public abstract int port(); + + public abstract String meth(); + + public abstract String path(); + + public abstract String agent(); + + public abstract AuthzEnv env(); + + public abstract void setLur(Lur lur); + + public abstract boolean fish(Permission p); + + public abstract boolean forceRequested(); + + public abstract Organization org(); + + public abstract boolean moveRequested(); + + public abstract boolean futureRequested(); + + public abstract void logAuditTrail(LogTarget lt); + +} diff --git a/authz-core/src/main/java/com/att/authz/env/AuthzTransFilter.java b/authz-core/src/main/java/com/att/authz/env/AuthzTransFilter.java new file mode 100644 index 00000000..46bec117 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/env/AuthzTransFilter.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.env; + +import java.security.Principal; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; + +import com.att.cadi.CadiException; +import com.att.cadi.Connector; +import com.att.cadi.TrustChecker; +import com.att.cadi.principal.BasicPrincipal; +import com.att.cadi.principal.TrustPrincipal; +import com.att.cadi.principal.X509Principal; +import com.att.cssa.rserv.TransFilter; +import com.att.inno.env.Env; +import com.att.inno.env.Slot; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans.Metric; + +public class AuthzTransFilter extends TransFilter { + private AuthzEnv env; + public Metric serviceMetric; + public static Slot transIDslot; + + public static final String TRANS_ID_SLOT = "TRANS_ID_SLOT"; + public static final int BUCKETSIZE = 2; + + public AuthzTransFilter(AuthzEnv env, Connector con, TrustChecker tc, Object ... additionalTafLurs) throws CadiException { + super(env,con, tc, additionalTafLurs); + this.env = env; + serviceMetric = new Metric(); + serviceMetric.buckets = new float[BUCKETSIZE]; + if(transIDslot==null) { + transIDslot = env.slot(TRANS_ID_SLOT); + } + } + + @Override + protected AuthzTrans newTrans() { + AuthzTrans at = env.newTrans(); + at.setLur(getLur()); + return at; + } + + @Override + protected TimeTaken start(AuthzTrans trans, ServletRequest request) { + trans.set((HttpServletRequest)request); + return trans.start("Trans " + //(context==null?"n/a":context.toString()) + + " IP: " + trans.ip() + + " Port: " + trans.port() + , Env.SUB); + } + + @Override + protected void authenticated(AuthzTrans trans, Principal p) { + trans.setUser(p); + } + + @Override + protected void tallyHo(AuthzTrans trans) { + if(trans.info().isLoggable()) { + // Transaction is done, now post + StringBuilder sb = new StringBuilder("AuditTrail\n"); + // We'll grabAct sub-metrics for Remote Calls and JSON + // IMPORTANT!!! if you add more entries here, change "BUCKETSIZE"!!! + Metric m = trans.auditTrail(1, sb, Env.REMOTE,Env.JSON); + + // Add current Metrics to total metrics + serviceMetric.total+= m.total; + for(int i=0;i { + private AuthzEnv env; + public Metric serviceMetric; + + public static final int BUCKETSIZE = 2; + + public AuthzTransOnlyFilter(AuthzEnv env) { + this.env = env; + serviceMetric = new Metric(); + serviceMetric.buckets = new float[BUCKETSIZE]; + } + + @Override + protected AuthzTrans newTrans() { + return env.newTrans(); + } + + @Override + protected TimeTaken start(AuthzTrans trans, ServletRequest request) { + trans.set((HttpServletRequest)request); + return trans.start("Trans " + //(context==null?"n/a":context.toString()) + + " IP: " + trans.ip() + + " Port: " + trans.port() + , Env.SUB); + } + + @Override + protected void authenticated(AuthzTrans trans, Principal p) { + trans.setUser(p); + } + + @Override + protected void tallyHo(AuthzTrans trans) { + // Transaction is done, now post + StringBuilder sb = new StringBuilder("AuditTrail\n"); + // We'll grab sub-metrics for Remote Calls and JSON + // IMPORTANT!!! if you add more entries here, change "BUCKETSIZE"!!! + Metric m = trans.auditTrail(1, sb, Env.REMOTE,Env.JSON); + // Add current Metrics to total metrics + serviceMetric.total+= m.total; + for(int i=0;i T get(Slot slot, T deflt) { + return null; + } + @Override + public T get(StaticSlot slot, T dflt) { + return null; + } + @Override + public void setUser(Principal p) { + } + @Override + public Slot slot(String name) { + return null; + } + @Override + public AuthzEnv env() { + return null; + } + @Override + public String agent() { + return null; + } + + @Override + public void setLur(Lur lur) { + } + + @Override + public boolean fish(Permission p) { + return false; + } + + @Override + public boolean forceRequested() { + return false; + } + + @Override + public boolean futureRequested() { + return false; + } + + @Override + public boolean moveRequested() { + return false; + } + + @Override + public Organization org() { + return Organization.NULL; + } + + @Override + public void logAuditTrail(LogTarget lt) { + } + + @Override + public Metric auditTrail(LogTarget lt, int indent, StringBuilder sb, int... flag) { + // TODO Auto-generated method stub + return null; + } + +} + diff --git a/authz-core/src/main/java/com/att/authz/layer/FacadeImpl.java b/authz-core/src/main/java/com/att/authz/layer/FacadeImpl.java new file mode 100644 index 00000000..07394454 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/layer/FacadeImpl.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.layer; + +import javax.servlet.http.HttpServletResponse; + +import com.att.inno.env.Data; +import com.att.inno.env.Data.TYPE; + + + +public abstract class FacadeImpl { + protected static final String IN = "in"; + + protected void setContentType(HttpServletResponse response, TYPE type) { + response.setContentType(type==Data.TYPE.JSON?"application/json":"text.xml"); + } +} diff --git a/authz-core/src/main/java/com/att/authz/layer/Result.java b/authz-core/src/main/java/com/att/authz/layer/Result.java new file mode 100644 index 00000000..1af9a26b --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/layer/Result.java @@ -0,0 +1,326 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.layer; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + + +/** + * It would be nice if Java Enums were extensible, but they're not. + * + * + */ +public class Result { + private static final String SUCCESS = "Success"; + public static final String[] EMPTY_VARS = new String[0]; + + public final static int OK=0, + ERR_Security = 1, + ERR_Denied = 2, + ERR_Policy = 3, + ERR_BadData = 4, + ERR_NotImplemented = 5, + ERR_NotFound = 6, + ERR_ConflictAlreadyExists = 7, + ERR_ActionNotCompleted = 8, + ERR_Backend = 9, + ERR_General = 20; + + public final RV value; + public final int status; + public final String details; + public final String[] variables; + + protected Result(RV value, int status, String details, String[] variables) { + this.value = value; + if(value==null) { + specialCondition|=EMPTY_LIST; + } + this.status = status; + this.details = details; + if(variables==null) { + this.variables = EMPTY_VARS; + } else { + this.variables=variables; + } + } + + /** + * Create a Result class with "OK" status and "Success" for details + * + * This is the easiest to use + * + * @param value + * @param status + * @return + */ + public static Result ok(R value) { + return new Result(value,OK,SUCCESS,null); + } + + /** + * Accept Arrays and mark as empty or not + * @param value + * @return + */ + public static Result ok(R value[]) { + return new Result(value,OK,SUCCESS,null).emptyList(value.length==0); + } + + /** + * Accept Sets and mark as empty or not + * @param value + * @return + */ + public static Result> ok(Set value) { + return new Result>(value,OK,SUCCESS,null).emptyList(value.size()==0); + } + + /** + * Accept Lists and mark as empty or not + * @param value + * @return + */ + public static Result> ok(List value) { + return new Result>(value,OK,SUCCESS,null).emptyList(value.size()==0); + } + + /** + * Accept Collections and mark as empty or not + * @param value + * @return + */ + public static Result> ok(Collection value) { + return new Result>(value,OK,SUCCESS,null).emptyList(value.size()==0); + } + + + /** + * Special Case for Void Type + * @return + */ + public static Result ok() { + return new Result(null,OK,SUCCESS,null); + } + + /** + * Create a Status (usually non OK, with a details statement + * @param value + * @param status + * @param details + * @return + */ +// public static Result err(int status, String details) { +// return new Result(null,status,details,null); +// } + + /** + * Create a Status (usually non OK, with a details statement and variables supported + * @param status + * @param details + * @param variables + * @return + */ + public static Result err(int status, String details, String ... variables) { + return new Result(null,status,details,variables); + } + + /** + * Create Error from status and Details of previous Result (and not data) + * @param pdr + * @return + */ + public static Result err(Result pdr) { + return new Result(null,pdr.status,pdr.details,pdr.variables); + } + + /** + * Create General Error from Exception + * @param e + * @return + */ + public static Result err(Exception e) { + return new Result(null,ERR_General,e.getMessage(),EMPTY_VARS); + } + + /** + * Create a Status (usually non OK, with a details statement + * @param value + * @param status + * @param details + * @return + */ + public static Result create(R value, int status, String details, String ... vars) { + return new Result(value,status,details,vars); + } + + /** + * Create a Status from a previous status' result/details + * @param value + * @param status + * @param details + * @return + */ + public static Result create(R value, Result result) { + return new Result(value,result.status,result.details,result.variables); + } + + private static final int PARTIAL_CONTENT = 0x001; + private static final int EMPTY_LIST = 0x002; + + /** + * AAF Specific problems, etc + * + * + */ + + /** + * specialCondition is a bit field to enable multiple conditions, e.g. PARTIAL_CONTENT + */ + private int specialCondition = 0; + + + /** + * Is result set only partial results, i.e. the DAO clipped the real result set to a smaller number. + * @return true iff result returned PARTIAL_CONTENT + */ + public boolean partialContent() { + return (specialCondition & PARTIAL_CONTENT) == PARTIAL_CONTENT; + } + + /** + * Set fact that result set only returned partial results, i.e. the DAO clipped the real result set to a smaller number. + * @param hasPartialContent set true iff result returned PARTIAL_CONTENT + * @return this Result object, so you can chain calls, in builder style + */ + public Result partialContent(boolean hasPartialContent) { + if (hasPartialContent) { + specialCondition |= PARTIAL_CONTENT; + } else { + specialCondition &= (~PARTIAL_CONTENT); + } + return this; + } + + /** + * When Result is a List, you can check here to see if it's empty instead of looping + * + * @return + */ + public boolean isEmpty() { + return (specialCondition & EMPTY_LIST) == EMPTY_LIST; + } + + /** + * A common occurrence is that data comes back, but list is empty. If set, you can skip looking + * at list at the outset. + * + * @param emptyList + * @return + */ + public Result emptyList(boolean emptyList) { + if (emptyList) { + specialCondition |= EMPTY_LIST; + } else { + specialCondition &= (~EMPTY_LIST); + } + return this; + } + + + /** + * Convenience function. Checks OK, and also if List is not Empty + * Not valid if Data is not a List + * @return + */ + public boolean isOK() { + return status == OK; + } + + /** + * Convenience function. Checks OK, and also if List is not Empty + * Not valid if Data is not a List + * @return + */ + public boolean notOK() { + return status != OK; + } + + /** + * Convenience function. Checks OK, and also if List is not Empty + * Not valid if Data is not a List + * @return + */ + public boolean isOKhasData() { + return status == OK && (specialCondition & EMPTY_LIST) != EMPTY_LIST; + } + + + /** + * Convenience function. Checks OK, and also if List is not Empty + * Not valid if Data is not a List + * @return + */ + public boolean notOKorIsEmpty() { + return status != OK || (specialCondition & EMPTY_LIST) == EMPTY_LIST; + } + + @Override + public String toString() { + if(status==0) { + return details; + } else { + StringBuilder sb = new StringBuilder(); + sb.append(status); + sb.append(':'); + sb.append(String.format(details,((Object[])variables))); + if(isEmpty()) { + sb.append("{empty}"); + } + sb.append('-'); + sb.append(value.toString()); + return sb.toString(); + } + } + + public String errorString() { + StringBuilder sb = new StringBuilder(); + switch(status) { + case 1: sb.append("Security"); break; + case 2: sb.append("Denied"); break; + case 3: sb.append("Policy"); break; + case 4: sb.append("BadData"); break; + case 5: sb.append("NotImplemented"); break; + case 6: sb.append("NotFound"); break; + case 7: sb.append("AlreadyExists"); break; + case 8: sb.append("ActionNotComplete"); break; + default: sb.append("Error"); + } + sb.append(" - "); + sb.append(String.format(details, (Object[])variables)); + return sb.toString(); + } +} diff --git a/authz-core/src/main/java/com/att/authz/local/AbsData.java b/authz-core/src/main/java/com/att/authz/local/AbsData.java new file mode 100644 index 00000000..cc00efff --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/local/AbsData.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.local; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Iterator; + +import com.att.authz.env.AuthzTrans; +import com.att.authz.local.DataFile.Token; +import com.att.authz.local.DataFile.Token.Field; +import com.att.inno.env.Env; +import com.att.inno.env.TimeTaken; + +public abstract class AbsData implements Iterable { + protected DataFile data; + protected TextIndex ti; + private File dataf,idxf,lockf; + private String name; + private char delim; + private int maxLineSize; + private int fieldOffset; + private int skipLines; + + public AbsData(File dataf,char sepChar, int maxLineSize, int fieldOffset) { + File dir = dataf.getParentFile(); + int dot = dataf.getName().lastIndexOf('.'); + if(dot>=0) { + name = dataf.getName().substring(0,dot); + } + + this.dataf=dataf; + this.delim = sepChar; + this.maxLineSize = maxLineSize; + this.fieldOffset = fieldOffset; + idxf = new File(dir,name.concat(".idx")); + lockf = new File(dir,name.concat(".lock")); + + + data = new DataFile(dataf,"r"); + ti = new TextIndex(idxf); + skipLines=0; + } + + public void skipLines(int lines) { + skipLines=lines; + } + + public String name() { + return name; + } + + public void open(AuthzTrans trans, long timeout) throws IOException { + TimeTaken tt = trans.start("Open Data File", Env.SUB); + boolean opened = false, first = true; + try { + if(!dataf.exists()) { + throw new FileNotFoundException("Data File Missing:" + dataf.getCanonicalPath()); + } + long begin = System.currentTimeMillis(); + long end = begin+timeout; + boolean exists; + while((exists=lockf.exists()) && beginidxf.lastModified()) { + trans.warn().log(idxf.getCanonicalPath(),"is missing, empty or out of date, creating"); + RandomAccessFile raf = new RandomAccessFile(lockf, "rw"); + try { + ti.create(trans, data, maxLineSize, delim, fieldOffset, skipLines); + if(!idxf.exists() || (idxf.length()==0 && dataf.length()!=0)) { + throw new IOException("Data Index File did not create correctly"); + } + } finally { + raf.close(); + lockf.delete(); + } + } + } + + public void close(AuthzTrans trans) throws IOException { + ti.close(); + data.close(); + } + + public class Reuse { + private Token tokenData; + private Field fieldData; + + private Reuse(int size,char delim) { + tokenData = data.new Token(size); + fieldData = getTokenData().new Field(delim); + } + + public void reset() { + getFieldData().reset(); + } + + public void pos(int rec) { + getFieldData().reset(); + getTokenData().pos(rec); + } + + public String next() { + return getFieldData().next(); + } + + public String at(int field) { + return getFieldData().at(field); + } + + public String atToEnd(int field) { + return getFieldData().atToEnd(field); + } + + public Field getFieldData() { + return fieldData; + } + + public Token getTokenData() { + return tokenData; + } + + } + + public Reuse reuse() { + return new Reuse(maxLineSize,delim); + } + + public Iter iterator() { + return new Iter(); + } + + public class Iter implements Iterator { + private Reuse reuse; + private com.att.authz.local.TextIndex.Iter tii; + + public Iter() { + reuse = reuse(); + tii = ti.new Iter(); + } + + @Override + public boolean hasNext() { + return tii.hasNext(); + } + + @Override + public String next() { + reuse.reset(); + int rec = tii.next(); + reuse.pos(rec); + return reuse.at(0); + } + + @Override + public void remove() { + // read only + } + } +} diff --git a/authz-core/src/main/java/com/att/authz/local/DataFile.java b/authz-core/src/main/java/com/att/authz/local/DataFile.java new file mode 100644 index 00000000..a54e8563 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/local/DataFile.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.local; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; + +public class DataFile { + private RandomAccessFile rafile; + private FileChannel channel; + public MappedByteBuffer mapBuff; + private final File file; + private final String access; + + public DataFile(File file, String access) { + this.file = file; + this.access = access; + } + public void open() throws IOException { + if(!file.exists()) throw new FileNotFoundException(); + rafile = new RandomAccessFile(file,access); + channel = rafile.getChannel(); + mapBuff = channel.map("r".equals(access)?MapMode.READ_ONLY:MapMode.READ_WRITE,0,channel.size()); + } + public void close() throws IOException { + if(channel!=null){channel.close();} + if(rafile!=null) {rafile.close();} + mapBuff = null; + } + + public long size() throws IOException { + return channel.size(); + } + + private synchronized int load(Token t) { + int len = Math.min(mapBuff.limit()-t.next,t.buff.length); + if(len>0) { + mapBuff.position(t.next); + mapBuff.get(t.buff,0,len); + } + return len<0?0:len; + } + + public class Token { + private byte[] buff; + int pos, next, end; + + public Token(int size) { + buff = new byte[size]; + pos = next = end = 0; + } + + public boolean pos(int to) { + pos = next = to; + return (end=load(this))>0; + } + + public boolean nextLine() { + end = load(this); + pos = next; + for(int i=0;i=end)return null; + int start = idx; + byte c=0; + int endStr = -1; + while(idx=end)?1:0))); + } + + } + + public int pos() { + return pos; + } + } + + public File file() { + return file; + } + +} diff --git a/authz-core/src/main/java/com/att/authz/local/TextIndex.java b/authz-core/src/main/java/com/att/authz/local/TextIndex.java new file mode 100644 index 00000000..a6899642 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/local/TextIndex.java @@ -0,0 +1,253 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.local; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import com.att.authz.local.DataFile.Token; +import com.att.authz.local.DataFile.Token.Field; +import com.att.inno.env.Env; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans; + +public class TextIndex { + private static final int REC_SIZE=8; + + private File file; + private DataFile dataFile=null; + + public TextIndex(File theFile) { + file = theFile; + } + + public void open() throws IOException { + dataFile = new DataFile(file,"r"); + dataFile.open(); + } + + public void close() throws IOException { + if(dataFile!=null) {dataFile.close();} + } + + public int find(Object key, AbsData.Reuse reuse, int offset) throws IOException { + return find(key,reuse.getTokenData(),reuse.getFieldData(),offset); + } + + public int find(Object key, DataFile.Token dtok, Field df, int offset) throws IOException { + if(dataFile==null) {throw new IOException("File not opened");} + long hash = hashToLong(key.hashCode()); + int min=0, max = (int)(dataFile.size()/REC_SIZE); + Token ttok = dataFile.new Token(REC_SIZE); + IntBuffer tib = ttok.getIntBuffer(); + long lhash; + int curr; + while((max-min)>100) { + ttok.pos((curr=(min+(max-min)/2))*REC_SIZE); + tib.rewind(); + lhash = hashToLong(tib.get()); + if(lhashhash) { + max=curr-1; + } else { + min=curr-40; + max=curr+40; + break; + } + } + + List entries = new ArrayList(); + for(int i=min;i<=max;++i) { + ttok.pos(i*REC_SIZE); + tib.rewind(); + lhash = hashToLong(tib.get()); + if(lhash==hash) { + entries.add(tib.get()); + } else if(lhash>hash) { + break; + } + } + + for(Integer i : entries) { + dtok.pos(i); + if(df.at(offset).equals(key)) { + return i; + } + } + return -1; + } + + + /* + * Have to change Bytes into a Long, to avoid the inevitable signs in the Hash + */ + private static long hashToLong(int hash) { + long rv; + if(hash<0) { + rv = 0xFFFFFFFFL & hash; + } else { + rv = hash; + } + return rv; + } + + public void create(final Trans trans,final DataFile data, int maxLine, char delim, int fieldOffset, int skipLines) throws IOException { + RandomAccessFile raf; + FileChannel fos; + + List list = new LinkedList(); // Some hashcodes will double... DO NOT make a set + TimeTaken tt2 = trans.start("Open Files", Env.SUB); + try { + raf = new RandomAccessFile(file,"rw"); + raf.setLength(0L); + fos = raf.getChannel(); + } finally { + tt2.done(); + } + + try { + + Token t = data.new Token(maxLine); + Field f = t.new Field(delim); + + int count = 0; + if(skipLines>0) { + trans.info().log("Skipping",skipLines,"line"+(skipLines==1?" in":"s in"),data.file().getName()); + } + for(int i=0;i { + public int hash, pos; + public Idx(Object obj, int pos) { + hash = obj.hashCode(); + this.pos = pos; + } + + @Override + public int compareTo(Idx ib) { + long a = hashToLong(hash); + long b = hashToLong(ib.hash); + return a>b?1:a 1, remove User from Owner + // if # of Owners = 1, changeOwner to X Remove Owner???? + boolean hasPermission(String user, String ns, String type, String instance, String action); + boolean inRole(String name); + + public String namespace() throws Exception; + public String id(); +} diff --git a/authz-core/src/main/java/com/att/authz/org/Organization.java b/authz-core/src/main/java/com/att/authz/org/Organization.java new file mode 100644 index 00000000..e8938cdc --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/org/Organization.java @@ -0,0 +1,491 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.org; + +import java.util.ArrayList; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.att.authz.env.AuthzTrans; + +/** + * Organization + * + * There is Organizational specific information required which we have extracted to a plugin + * + * It supports using Company Specific User Directory lookups, as well as supporting an + * Approval/Validation Process to simplify control of Roles and Permissions for large organizations + * in lieu of direct manipulation by a set of Admins. + * + * + */ +public interface Organization { + public static final String N_A = "n/a"; + + public interface Identity { + public String id(); + public String fullID(); // Fully Qualified ID (includes Domain of Organization) + public String type(); // Must be one of "IdentityTypes", see below + public String responsibleTo(); // Chain of Command, Comma Separated if required + public List delegate(); // Someone who has authority to act on behalf of Identity + public String email(); + public String fullName(); + public boolean isResponsible(); // Is id passed belong to a person suitable to be Responsible for content Management + public boolean isFound(); // Is Identity found in Identity stores + public Identity owner() throws OrganizationException; // Identity is directly responsible for App ID + public Organization org(); // Organization of Identity + } + + + /** + * Name of Organization, suitable for Logging + * @return + */ + public String getName(); + + /** + * Realm, for use in distinguishing IDs from different systems/Companies + * @return + */ + public String getRealm(); + + String getDomain(); + + /** + * Get Identity information based on userID + * + * @param id + * @return + */ + public Identity getIdentity(AuthzTrans trans, String id) throws OrganizationException; + + + /** + * Does the ID pass Organization Standards + * + * Return a Blank (empty) String if empty, otherwise, return a "\n" separated list of + * reasons why it fails + * + * @param id + * @return + */ + public String isValidID(String id); + + /** + * Return a Blank (empty) String if empty, otherwise, return a "\n" separated list of + * reasons why it fails + * + * Identity is passed in to allow policies regarding passwords that are the same as user ID + * + * any entries for "prev" imply a reset + * + * @param id + * @param password + * @return + */ + public String isValidPassword(String user, String password, String ... prev); + + + /** + * Does your Company distinguish essential permission structures by kind of Identity? + * i.e. Employee, Contractor, Vendor + * @return + */ + public Set getIdentityTypes(); + + public enum Notify { + Approval(1), + PasswordExpiration(2), + RoleExpiration(3); + + final int id; + Notify(int id) {this.id = id;} + public int getValue() {return id;} + public static Notify from(int type) { + for(Notify t : Notify.values()) { + if(t.id==type) { + return t; + } + } + return null; + } + } + + public enum Response{ + OK, + ERR_NotImplemented, + ERR_UserNotExist, + ERR_NotificationFailure, + }; + + public enum Expiration { + Password, + TempPassword, + Future, + UserInRole, + UserDelegate, + ExtendPassword + } + + public enum Policy { + CHANGE_JOB, + LEFT_COMPANY, + CREATE_MECHID, + CREATE_MECHID_BY_PERM_ONLY, + OWNS_MECHID, + AS_EMPLOYEE, + MAY_EXTEND_CRED_EXPIRES + } + + /** + * Notify a User of Action or Info + * + * @param type + * @param url + * @param users (separated by commas) + * @param ccs (separated by commas) + * @param summary + */ + + public Response notify(AuthzTrans trans, Notify type, String url, String ids[], String ccs[], String summary, Boolean urgent); + + /** + * (more) generic way to send an email + * + * @param toList + * @param ccList + * @param subject + * @param body + * @param urgent + */ + + public int sendEmail(AuthzTrans trans, List toList, List ccList, String subject, String body, Boolean urgent) throws OrganizationException; + + /** + * whenToValidate + * + * Authz support services will ask the Organization Object at startup when it should + * kickoff Validation processes given particular types. + * + * This allows the Organization to express Policy + * + * Turn off Validation behavior by returning "null" + * + */ + public Date whenToValidate(Notify type, Date lastValidated); + + + /** + * Expiration + * + * Given a Calendar item of Start (or now), set the Expiration Date based on the Policy + * based on type. + * + * For instance, "Passwords expire in 3 months" + * + * The Extra Parameter is used by certain Orgs. + * + * For Password, the extra is UserID, so it can check the Identity Type + * + * @param gc + * @param exp + * @return + */ + public GregorianCalendar expiration(GregorianCalendar gc, Expiration exp, String ... extra); + + /** + * Get Email Warning timing policies + * @return + */ + public EmailWarnings emailWarningPolicy(); + + /** + * + * @param trans + * @param user + * @return + */ + public List getApprovers(AuthzTrans trans, String user) throws OrganizationException ; + + /* + * + * @param user + * @param type + * @param users + * @return + public Response notifyRequest(AuthzTrans trans, String user, Approval type, List approvers); + */ + + /** + * + * @return + */ + public String getApproverType(); + + /* + * startOfDay - define for company what hour of day business starts (specifically for password and other expiration which + * were set by Date only.) + * + * @return + */ + public int startOfDay(); + + /** + * implement this method to support any IDs that can have multiple entries in the cred table + * NOTE: the combination of ID/expiration date/(encryption type when implemented) must be unique. + * Since expiration date is based on startOfDay for your company, you cannot create many + * creds for the same ID in the same day. + * @param id + * @return + */ + public boolean canHaveMultipleCreds(String id); + + /** + * + * @param id + * @return + */ + public boolean isValidCred(String id); + + /** + * If response is Null, then it is valid. Otherwise, the Organization specific reason is returned. + * + * @param trans + * @param policy + * @param executor + * @param vars + * @return + * @throws OrganizationException + */ + public String validate(AuthzTrans trans, Policy policy, Executor executor, String ... vars) throws OrganizationException; + + boolean isTestEnv(); + + public void setTestMode(boolean dryRun); + + public static final Organization NULL = new Organization() + { + private final GregorianCalendar gc = new GregorianCalendar(1900, 1, 1); + private final List nullList = new ArrayList(); + private final Set nullStringSet = new HashSet(); + private final Identity nullIdentity = new Identity() { + List nullIdentity = new ArrayList(); + @Override + public String type() { + return N_A; + } + @Override + public String responsibleTo() { + return N_A; + } + @Override + public boolean isResponsible() { + return false; + } + + @Override + public boolean isFound() { + return false; + } + + @Override + public String id() { + return N_A; + } + + @Override + public String fullID() { + return N_A; + } + + @Override + public String email() { + return N_A; + } + + @Override + public List delegate() { + return nullIdentity; + } + @Override + public String fullName() { + return N_A; + } + @Override + public Identity owner() { + return null; + } + @Override + public Organization org() { + return NULL; + } + }; + + @Override + public String getName() { + return N_A; + } + + @Override + public String getRealm() { + return N_A; + } + + @Override + public String getDomain() { + return N_A; + } + + @Override + public Identity getIdentity(AuthzTrans trans, String id) { + return nullIdentity; + } + + @Override + public String isValidID(String id) { + return N_A; + } + + @Override + public String isValidPassword(String user, String password,String... prev) { + return N_A; + } + + @Override + public Set getIdentityTypes() { + return nullStringSet; + } + + @Override + public Response notify(AuthzTrans trans, Notify type, String url, + String[] users, String[] ccs, String summary, Boolean urgent) { + return Response.ERR_NotImplemented; + } + + @Override + public int sendEmail(AuthzTrans trans, List toList, List ccList, + String subject, String body, Boolean urgent) throws OrganizationException { + return 0; + } + + @Override + public Date whenToValidate(Notify type, Date lastValidated) { + return gc.getTime(); + } + + @Override + public GregorianCalendar expiration(GregorianCalendar gc, + Expiration exp, String... extra) { + return gc==null?new GregorianCalendar():gc; + } + + @Override + public List getApprovers(AuthzTrans trans, String user) + throws OrganizationException { + return nullList; + } + + @Override + public String getApproverType() { + return ""; + } + + @Override + public int startOfDay() { + return 0; + } + + @Override + public boolean canHaveMultipleCreds(String id) { + return false; + } + + @Override + public boolean isValidCred(String id) { + return false; + } + + @Override + public String validate(AuthzTrans trans, Policy policy, Executor executor, String ... vars) + throws OrganizationException { + return "Null Organization rejects all Policies"; + } + + @Override + public boolean isTestEnv() { + return false; + } + + @Override + public void setTestMode(boolean dryRun) { + } + + @Override + public EmailWarnings emailWarningPolicy() { + return new EmailWarnings() { + + @Override + public long credEmailInterval() + { + return 604800000L; // 7 days in millis 1000 * 86400 * 7 + } + + @Override + public long roleEmailInterval() + { + return 604800000L; // 7 days in millis 1000 * 86400 * 7 + } + + @Override + public long apprEmailInterval() { + return 259200000L; // 3 days in millis 1000 * 86400 * 3 + } + + @Override + public long credExpirationWarning() + { + return( 2592000000L ); // One month, in milliseconds 1000 * 86400 * 30 in milliseconds + } + + @Override + public long roleExpirationWarning() + { + return( 2592000000L ); // One month, in milliseconds 1000 * 86400 * 30 in milliseconds + } + + @Override + public long emailUrgentWarning() + { + return( 1209600000L ); // Two weeks, in milliseconds 1000 * 86400 * 14 in milliseconds + } + + }; + } + }; +} + + diff --git a/authz-core/src/main/java/com/att/authz/org/OrganizationException.java b/authz-core/src/main/java/com/att/authz/org/OrganizationException.java new file mode 100644 index 00000000..a3928fb9 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/org/OrganizationException.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.org; + +public class OrganizationException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public OrganizationException() { + super(); + } + + public OrganizationException(String message) { + super(message); + } + + public OrganizationException(Throwable cause) { + super(cause); + } + + public OrganizationException(String message, Throwable cause) { + super(message, cause); + } + + public OrganizationException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/authz-core/src/main/java/com/att/authz/org/OrganizationFactory.java b/authz-core/src/main/java/com/att/authz/org/OrganizationFactory.java new file mode 100644 index 00000000..c97111e9 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/org/OrganizationFactory.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.org; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.att.authz.env.AuthzEnv; +import com.att.authz.env.AuthzTrans; +import com.att.inno.env.APIException; +import com.att.inno.env.Slot; + +/** + * Organization Plugin Mechanism + * + * Define a NameSpace for the company (i.e. com.att), and put in Properties as + * "Organization.[your NS" and assign the supporting Class. + * + * Example: + * Organization.com.att=com.att.authz.org.att.ATT + * + * + */ +public class OrganizationFactory { + public static final String ORG_SLOT = "ORG_SLOT"; + private static Organization defaultOrg = null; + private static Map orgs = new ConcurrentHashMap(); + private static Slot orgSlot; + + public static void setDefaultOrg(AuthzEnv env, String orgClass) throws APIException { + orgSlot = env.slot(ORG_SLOT); + try { + @SuppressWarnings("unchecked") + Class cls = (Class) Class.forName(orgClass); + Constructor cnst = cls.getConstructor(AuthzEnv.class); + defaultOrg = cnst.newInstance(env); + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | + InstantiationException | IllegalAccessException | IllegalArgumentException | + InvocationTargetException e) { + throw new APIException(e); + } + } + + public static Organization obtain(AuthzEnv env,String orgNS) throws OrganizationException { + int at = orgNS.indexOf('@'); + if(at<0) { + if(!orgNS.startsWith("com.")) { + int dot1; + if((dot1 = orgNS.lastIndexOf('.'))>-1) { + int dot2; + StringBuilder sb = new StringBuilder(); + if((dot2 = orgNS.lastIndexOf('.',dot1-1))>-1) { + sb.append(orgNS,dot1+1,orgNS.length()); + sb.append('.'); + sb.append(orgNS,dot2+1,dot1); + } else { + sb.append(orgNS,dot1+1,orgNS.length()); + sb.append('.'); + sb.append(orgNS,at+1,dot1); + } + orgNS=sb.toString(); + } + } + } else { + // Only use two places (Enterprise) of domain + int dot; + if((dot= orgNS.lastIndexOf('.'))>-1) { + StringBuilder sb = new StringBuilder(); + int dot2; + if((dot2 = orgNS.lastIndexOf('.',dot-1))>-1) { + sb.append(orgNS.substring(dot+1)); + sb.append(orgNS.subSequence(dot2, dot)); + orgNS = sb.toString(); + } else { + sb.append(orgNS.substring(dot+1)); + sb.append('.'); + sb.append(orgNS.subSequence(at+1, dot)); + orgNS = sb.toString(); + } + } + } + Organization org = orgs.get(orgNS); + if(org == null) { + String orgClass = env.getProperty("Organization."+orgNS); + if(orgClass == null) { + env.warn().log("There is no Organization." + orgNS + " property"); + } else { + for(Organization o : orgs.values()) { + if(orgClass.equals(o.getClass().getName())) { + org = o; + } + } + if(org==null) { + try { + @SuppressWarnings("unchecked") + Class cls = (Class) Class.forName(orgClass); + Constructor cnst = cls.getConstructor(AuthzEnv.class); + org = cnst.newInstance(env); + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | + InstantiationException | IllegalAccessException | IllegalArgumentException | + InvocationTargetException e) { + throw new OrganizationException(e); + } + } + orgs.put(orgNS, org); + } + if(org==null && defaultOrg!=null) { + org=defaultOrg; + orgs.put(orgNS, org); + } + } + + return org; + } + + public static void set(AuthzTrans trans, String orgNS) throws OrganizationException { + Organization org = obtain(trans.env(),orgNS); + trans.put(orgSlot, org); + } + + public static Organization get(AuthzTrans trans) { + return trans.get(orgSlot,defaultOrg); + } + +} diff --git a/authz-core/src/main/java/com/att/authz/server/AbsServer.java b/authz-core/src/main/java/com/att/authz/server/AbsServer.java new file mode 100644 index 00000000..aa935d09 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/server/AbsServer.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.authz.server; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.Principal; +import java.util.Properties; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; + +import com.att.authz.common.Define; +import com.att.authz.env.AuthzEnv; +import com.att.authz.env.AuthzTrans; +import com.att.cadi.CadiException; +import com.att.cadi.LocatorException; +//import com.att.cadi.PropAccess; +import com.att.cadi.aaf.v2_0.AAFConHttp; +import com.att.cadi.client.Rcli; +import com.att.cadi.client.Retryable; +import com.att.cadi.config.Config; +import com.att.cadi.http.HTransferSS; +import com.att.cssa.rserv.RServlet; +import com.att.inno.env.APIException; + +public abstract class AbsServer extends RServlet { + private static final String AAF_API_VERSION = "2.0"; + public final String app; + public final AuthzEnv env; + public AAFConHttp aafCon; + + public AbsServer(final AuthzEnv env, final String app) throws CadiException, GeneralSecurityException, IOException { + this.env = env; + this.app = app; + if(env.getProperty(Config.AAF_URL)!=null) { + //aafCon = new AAFConHttp(env); + } + } + + // This is a method, so we can overload for AAFAPI + public String aaf_url() { + return env.getProperty(Config.AAF_URL); + } + + public abstract void startDME2(Properties props) throws Exception; + public static void setup(Class abss, String propFile) { + + try { + // Load Properties from authFramework.properties. Needed for DME2 and AuthzEnv + Properties props = new Properties(); + URL rsrc = ClassLoader.getSystemResource(propFile); + if(rsrc==null) { + System.err.println("Folder containing " + propFile + " must be on Classpath"); + System.exit(1); + } + + InputStream is = rsrc.openStream(); + try { + props.load(is); + } finally { + is.close(); + is=null; + } + + // Load Properties into AuthzEnv + AuthzEnv env = new AuthzEnv(props); + // Log where Config found + env.init().log("Configuring from",rsrc.getPath()); + rsrc = null; + + // Print Cipher Suites Available + if(env.debug().isLoggable()) { + SSLContext context = SSLContext.getDefault(); + SSLSocketFactory sf = context.getSocketFactory(); + StringBuilder sb = new StringBuilder("Available Cipher Suites: "); + boolean first = true; + int count=0; + for( String cs : sf.getSupportedCipherSuites()) { + if(first)first = false; + else sb.append(','); + sb.append(cs); + if(++count%4==0){sb.append('\n');} + } + env.debug().log(sb); + } + + // Set ROOT NS, etc + Define.set(env); + + // Convert CADI properties and Encrypted Passwords for these two properties (if exist) + // to DME2 Readable. Further, Discovery Props are loaded to System if missing. + // May be causing client errors + //Config.cadiToDME2(env,props); + env.init().log("DME2 ServiceName: " + env.getProperty("DMEServiceName","unknown")); + + // Construct with Env + Constructor cons = abss.getConstructor(new Class[] {AuthzEnv.class}); + // Start DME2 (DME2 needs Properties form of props) + AbsServer s = (AbsServer)cons.newInstance(env); + + // Schedule removal of Clear Text Passwords from System Props (DME2 Requirement) +// new Timer("PassRemove").schedule(tt, 120000); +// tt=null; + + s.startDME2(props); + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(1); + } + } + + public Rcli client() throws CadiException { + return aafCon.client(AAF_API_VERSION); + } + + public Rcli clientAsUser(Principal p) throws CadiException { + return aafCon.client(AAF_API_VERSION).forUser( + new HTransferSS(p,app, aafCon.securityInfo())); + } + + public RET clientAsUser(Principal p,Retryable retryable) throws APIException, LocatorException, CadiException { + return aafCon.hman().best(new HTransferSS(p,app, aafCon.securityInfo()), retryable); + } + +} diff --git a/authz-core/src/main/java/com/att/cache/Cache.java b/authz-core/src/main/java/com/att/cache/Cache.java new file mode 100644 index 00000000..2930e09c --- /dev/null +++ b/authz-core/src/main/java/com/att/cache/Cache.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cache; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; + +import com.att.inno.env.Env; +import com.att.inno.env.Trans; + +/** + * Create and maintain a Map of Maps used for Caching + * + * + * @param + * @param + */ +public class Cache { + private static Clean clean; + private static Timer cleanseTimer; + + public static final String CACHE_HIGH_COUNT = "CACHE_HIGH_COUNT"; + public static final String CACHE_CLEAN_INTERVAL = "CACHE_CLEAN_INTERVAL"; +// public static final String CACHE_MIN_REFRESH_INTERVAL = "CACHE_MIN_REFRESH_INTERVAL"; + + private static final Map> cacheMap; + + static { + cacheMap = new HashMap>(); + } + + /** + * Dated Class - store any Data with timestamp + * + * + */ + public final static class Dated { + public Date timestamp; + public List data; + + public Dated(List data) { + timestamp = new Date(); + this.data = data; + } + + public Dated(T t) { + timestamp = new Date(); + ArrayList al = new ArrayList(1); + al.add(t); + data = al; + } + + public void touch() { + timestamp = new Date(); + } + } + + public static Map obtain(String key) { + Map m = cacheMap.get(key); + if(m==null) { + m = new ConcurrentHashMap(); + synchronized(cacheMap) { + cacheMap.put(key, m); + } + } + return m; + } + + /** + * Clean will examine resources, and remove those that have expired. + * + * If "highs" have been exceeded, then we'll expire 10% more the next time. This will adjust after each run + * without checking contents more than once, making a good average "high" in the minimum speed. + * + * + */ + private final static class Clean extends TimerTask { + private final Env env; + private Set set; + + // The idea here is to not be too restrictive on a high, but to Expire more items by + // shortening the time to expire. This is done by judiciously incrementing "advance" + // when the "highs" are exceeded. This effectively reduces numbers of cached items quickly. + private final int high; + private long advance; + private final long timeInterval; + + public Clean(Env env, long cleanInterval, int highCount) { + this.env = env; + high = highCount; + timeInterval = cleanInterval; + advance = 0; + set = new HashSet(); + } + + public synchronized void add(String key) { + set.add(key); + } + + public void run() { + int count = 0; + int total = 0; + // look at now. If we need to expire more by increasing "now" by "advance" + Date now = new Date(System.currentTimeMillis() + advance); + + + for(String name : set) { + Map map = cacheMap.get(name); + if(map!=null) for(Map.Entry me : map.entrySet()) { + ++total; + if(me.getValue().timestamp.before(now)) { + map.remove(me.getKey()); + ++count; + } + } +// if(count>0) { +// env.info().log(Level.INFO, "Cache removed",count,"expired",name,"Elements"); +// } + } + + if(count>0) { + env.info().log(Level.INFO, "Cache removed",count,"expired Cached Elements out of", total); + } + + // If High (total) is reached during this period, increase the number of expired services removed for next time. + // There's no point doing it again here, as there should have been cleaned items. + if(total>high) { + // advance cleanup by 10%, without getting greater than timeInterval. + advance = Math.min(timeInterval, advance+(timeInterval/10)); + } else { + // reduce advance by 10%, without getting lower than 0. + advance = Math.max(0, advance-(timeInterval/10)); + } + } + } + + public static synchronized void startCleansing(Env env, String ... keys) { + if(cleanseTimer==null) { + cleanseTimer = new Timer("Cache Cleanup Timer"); + int cleanInterval = Integer.parseInt(env.getProperty(CACHE_CLEAN_INTERVAL,"60000")); // 1 minute clean cycles + int highCount = Integer.parseInt(env.getProperty(CACHE_HIGH_COUNT,"5000")); + cleanseTimer.schedule(clean = new Clean(env, cleanInterval, highCount), cleanInterval, cleanInterval); + } + + for(String key : keys) { + clean.add(key); + } + } + + public static void stopTimer() { + if(cleanseTimer!=null) { + cleanseTimer.cancel(); + cleanseTimer = null; + } + } + + public static void addShutdownHook() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Cache.stopTimer(); + } + }); + } + +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java b/authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java new file mode 100644 index 00000000..ea2850aa --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/Acceptor.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.att.inno.env.Trans; + +/** + * Find Acceptable Paths and place them where TypeCode can evaluate. + * + * If there are more than one, TypeCode will choose based on "q" value + * + * @param + */ +class Acceptor { + private List, List>>>> types; + List, List>>>> acceptable; + + public Acceptor(List, List>>>> types) { + this.types = types; + acceptable = new ArrayList, List>>>>(); + } + + private boolean eval(HttpCode code, String str, List props) { +// int plus = str.indexOf('+'); +// if(plus<0) { + boolean ok = false; + boolean any = false; + for(Pair, List>>> type : types) { + ok = true; + if(type.x.equals(str)) { + for(Iterator iter = props.iterator();ok && iter.hasNext();) { + ok = props(type,iter.next(),iter.next()); + } + if(ok) { + any = true; + acceptable.add(type); + } + } + } +// } else { // Handle Accepts with "+" as in application/xaml+xml +// int prev = str.indexOf('/')+1; +// String first = str.substring(0,prev); +// String nstr; +// while(prev!=0) { +// nstr = first + (plus<0?str.substring(prev):str.substring(prev,plus)); +// +// for(Pair, List>>> type : types) { +// if(type.x.equals(nstr)) { +// acceptable.add(type); +// return type; +// } +// } +// prev = plus+1; +// plus=str.indexOf('+', prev); +// }; +// } + return any; + } + + /** + * Evaluate Properties + * @param type + * @param tag + * @param value + * @return + */ + private boolean props(Pair, List>>> type, String tag, String value) { + boolean rv = false; + if(type.y!=null) { + for(Pair prop : type.y.y){ + if(tag.equals(prop.x)) { + if(tag.equals("charset")) { + return prop.x==null?false:prop.y.equals(value.toLowerCase()); // return True if Matched + } else if(tag.equals("version")) { + return prop.y.equals(new Version(value)); // Note: Version Class knows Minor Version encoding + } else if(tag.equals(Content.Q)) { // replace Q value + try { + type.y.y.get(0).y=Float.parseFloat(value); + } catch (NumberFormatException e) { + rv=false; // need to do something to make Sonar happy. But nothing to do. + } + return true; + } else { + return value.equals(prop.y); + } + } + } + } + return rv; + } + + /** + * parse + * + * Note: I'm processing by index to avoid lots of memory creation, which speeds things + * up for this time critical section of code. + * @param code + * @param cntnt + * @return + */ + protected boolean parse(HttpCode code, String cntnt) { + byte bytes[] = cntnt.getBytes(); + + int cis,cie=-1,cend; + int sis,sie,send; + String name; + ArrayList props = new ArrayList(); + do { + // Clear these in case more than one Semi + props.clear(); // on loop, do not want mixed properties + name=null; + + cis = cie+1; // find comma start + while(ciscis && Character.isSpaceChar(bytes[cend-1]))--cend; + // Start SEMIS + sie=cis-1; + do { + sis = sie+1; // semi start is one after previous end + while(siscend || sie<0?cend:sie; // if the Semicolon is after the comma, or non-existent, use comma end, else keep + while(send>sis && Character.isSpaceChar(bytes[send-1]))--send; + if(name==null) { // first entry in Comma set is the name, not a property + name = new String(bytes,sis,send-sis); + } else { // We've looped past the first Semi, now process as properties + // If there are additional elements (more entities within Semi Colons) + // apply Properties + int eq = cntnt.indexOf('=',sis); + if(eq>sis && eq=cis); // End SEMI processing + // Now evaluate Comma set and return if true + if(eval(code,name,props))return true; // else loop again to check next comma + } while(cie>=0); // loop to next comma + return false; // didn't get even one match + } + +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java b/authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java new file mode 100644 index 00000000..80de2692 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/CachingFileAccess.java @@ -0,0 +1,477 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentSkipListMap; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.att.aft.dme2.internal.jetty.http.HttpStatus; +import com.att.inno.env.Env; +import com.att.inno.env.EnvJAXB; +import com.att.inno.env.LogTarget; +import com.att.inno.env.Store; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans; +/* + * CachingFileAccess + * + * + */ +public class CachingFileAccess extends HttpCode { + public static void setEnv(Store store, String[] args) { + for(int i=0;i typeMap; + private final NavigableMap content; + private final Set attachOnly; + private final static String WEB_DIR_DEFAULT = "theme"; + public final static String CFA_WEB_DIR = "CFA_WebPath"; + // when to re-validate from file + // Re validating means comparing the Timestamp on the disk, and seeing it has changed. Cache is not marked + // dirty unless file has changed, but it still makes File IO, which for some kinds of cached data, i.e. + // deployed GUI elements is unnecessary, and wastes time. + // This parameter exists to cover the cases where data can be more volatile, so the user can choose how often the + // File IO will be accessed, based on probability of change. "0", of course, means, check every time. + private final static String CFA_CACHE_CHECK_INTERVAL = "CFA_CheckIntervalMS"; + private final static String CFA_MAX_SIZE = "CFA_MaxSize"; // Cache size limit + private final static String CFA_CLEAR_COMMAND = "CFA_ClearCommand"; + + // Note: can be null without a problem, but included + // to tie in with existing Logging. + public LogTarget logT = null; + public long checkInterval; // = 600000L; // only check if not hit in 10 mins by default + public int maxItemSize; // = 512000; // max file 500k + private Timer timer; + private String web_path; + // A command key is set in the Properties, preferably changed on deployment. + // it is compared at the beginning of the path, and if so, it is assumed to issue certain commands + // It's purpose is to protect, to some degree the command, even though it is HTTP, allowing + // local batch files to, for instance, clear caches on resetting of files. + private String clear_command; + + public CachingFileAccess(EnvJAXB env, String ... args) { + super(null,"Caching File Access"); + setEnv(env,args); + content = new ConcurrentSkipListMap(); // multi-thread changes possible + + attachOnly = new HashSet(); // short, unchanged + + typeMap = new TreeMap(); // Structure unchanged after Construction + typeMap.put("ico","image/icon"); + typeMap.put("html","text/html"); + typeMap.put("css","text/css"); + typeMap.put("js","text/javascript"); + typeMap.put("txt","text/plain"); + typeMap.put("xml","text/xml"); + typeMap.put("xsd","text/xml"); + attachOnly.add("xsd"); + typeMap.put("crl", "application/x-pkcs7-crl"); + typeMap.put("appcache","text/cache-manifest"); + + typeMap.put("json","text/json"); + typeMap.put("ogg", "audio/ogg"); + typeMap.put("jpg","image/jpeg"); + typeMap.put("gif","image/gif"); + typeMap.put("png","image/png"); + typeMap.put("svg","image/svg+xml"); + typeMap.put("jar","application/x-java-applet"); + typeMap.put("jnlp", "application/x-java-jnlp-file"); + typeMap.put("class", "application/java"); + + timer = new Timer("Caching Cleanup",true); + timer.schedule(new Cleanup(content,500),60000,60000); + + // Property params + web_path = env.getProperty(CFA_WEB_DIR,WEB_DIR_DEFAULT); + Object obj; + obj = env.get(env.staticSlot(CFA_CACHE_CHECK_INTERVAL),600000L); // Default is 10 mins + if(obj instanceof Long) {checkInterval=(Long)obj; + } else {checkInterval=Long.parseLong((String)obj);} + + obj = env.get(env.staticSlot(CFA_MAX_SIZE), 512000); // Default is max file 500k + if(obj instanceof Integer) {maxItemSize=(Integer)obj; + } else {maxItemSize =Integer.parseInt((String)obj);} + + clear_command = env.getProperty(CFA_CLEAR_COMMAND,null); + } + + + + @Override + public void handle(TRANS trans, HttpServletRequest req, HttpServletResponse resp) throws IOException { + String key = pathParam(req, ":key"); + if(key.equals(clear_command)) { + String cmd = pathParam(req,":cmd"); + resp.setHeader("Content-type",typeMap.get("txt")); + if("clear".equals(cmd)) { + content.clear(); + resp.setStatus(HttpStatus.OK_200); + } else { + resp.setStatus(HttpStatus.BAD_REQUEST_400); + } + return; + } + Content c = load(logT , web_path,key, null, checkInterval); + if(c.attachmentOnly) { + resp.setHeader("Content-disposition", "attachment"); + } + c.write(resp.getOutputStream()); + c.setHeader(resp); + trans.checkpoint(req.getPathInfo()); + } + + + public String webPath() { + return web_path; + } + + /** + * Reset the Cleanup size and interval + * + * The size and interval when started are 500 items (memory size unknown) checked every minute in a background thread. + * + * @param size + * @param interval + */ + public void cleanupParams(int size, long interval) { + timer.cancel(); + timer.schedule(new Cleanup(content,size), interval, interval); + } + + + + /** + * Load a file, first checking cache + * + * + * @param logTarget - logTarget can be null (won't log) + * @param dataRoot - data root storage directory + * @param key - relative File Path + * @param mediaType - what kind of file is it. If null, will check via file extension + * @param timeCheck - "-1" will take system default - Otherwise, will compare "now" + timeCheck(Millis) before looking at File mod + * @return + * @throws IOException + */ + public Content load(LogTarget logTarget, String dataRoot, String key, String mediaType, long _timeCheck) throws IOException { + long timeCheck = _timeCheck; + if(timeCheck<0) { + timeCheck=checkInterval; // if time < 0, then use default + } + String fileName = dataRoot + '/' + key; + Content c = content.get(key); + long systime = System.currentTimeMillis(); + File f=null; + if(c!=null) { + // Don't check every hit... only after certain time value + if(c.date < systime + timeCheck) { + f = new File(fileName); + if(f.lastModified()>c.date) { + c=null; + } + } + } + if(c==null) { + if(logTarget!=null) { + logTarget.log("File Read: ",key); + } + + if(f==null){ + f = new File(fileName); + } + + boolean cacheMe; + if(f.exists()) { + if(f.length() > maxItemSize) { + c = new DirectFileContent(f); + cacheMe = false; + } else { + c = new CachedContent(f); + cacheMe = checkInterval>0; + } + + if(mediaType==null) { // determine from file Ending + int idx = key.lastIndexOf('.'); + String subkey = key.substring(++idx); + if((c.contentType = idx<0?null:typeMap.get(subkey))==null) { + // if nothing else, just set to default type... + c.contentType = "application/octet-stream"; + } + c.attachmentOnly = attachOnly.contains(subkey); + } else { + c.contentType=mediaType; + c.attachmentOnly = false; + } + + c.date = f.lastModified(); + + if(cacheMe) { + content.put(key, c); + } + } else { + c=NULL; + } + } else { + if(logTarget!=null)logTarget.log("Cache Read: ",key); + } + + // refresh hit time + c.access = systime; + return c; + } + + public Content loadOrDefault(Trans trans, String targetDir, String targetFileName, String sourcePath, String mediaType) throws IOException { + try { + return load(trans.info(),targetDir,targetFileName,mediaType,0); + } catch(FileNotFoundException e) { + String targetPath = targetDir + '/' + targetFileName; + TimeTaken tt = trans.start("File doesn't exist; copy " + sourcePath + " to " + targetPath, Env.SUB); + try { + FileInputStream sourceFIS = new FileInputStream(sourcePath); + FileChannel sourceFC = sourceFIS.getChannel(); + File targetFile = new File(targetPath); + targetFile.getParentFile().mkdirs(); // ensure directory exists + FileOutputStream targetFOS = new FileOutputStream(targetFile); + try { + ByteBuffer bb = ByteBuffer.allocate((int)sourceFC.size()); + sourceFC.read(bb); + bb.flip(); // ready for reading + targetFOS.getChannel().write(bb); + } finally { + sourceFIS.close(); + targetFOS.close(); + } + } finally { + tt.done(); + } + return load(trans.info(),targetDir,targetFileName,mediaType,0); + } + } + + public void invalidate(String key) { + content.remove(key); + } + + private static final Content NULL=new Content() { + + @Override + public void setHeader(HttpServletResponse resp) { + resp.setStatus(HttpStatus.NOT_FOUND_404); + resp.setHeader("Content-type","text/plain"); + } + + @Override + public void write(Writer writer) throws IOException { + } + + @Override + public void write(OutputStream os) throws IOException { + } + + }; + + private static abstract class Content { + private long date; // date of the actual artifact (i.e. File modified date) + private long access; // last accessed + + protected String contentType; + protected boolean attachmentOnly; + + public void setHeader(HttpServletResponse resp) { + resp.setStatus(HttpStatus.OK_200); + resp.setHeader("Content-type",contentType); + resp.setHeader("Cache-Control", MAX_AGE); + } + + public abstract void write(Writer writer) throws IOException; + public abstract void write(OutputStream os) throws IOException; + + } + + private static class DirectFileContent extends Content { + private File file; + public DirectFileContent(File f) { + file = f; + } + + public String toString() { + return file.getName(); + } + + public void write(Writer writer) throws IOException { + FileReader fr = new FileReader(file); + char[] buff = new char[1024]; + try { + int read; + while((read = fr.read(buff,0,1024))>=0) { + writer.write(buff,0,read); + } + } finally { + fr.close(); + } + } + + public void write(OutputStream os) throws IOException { + FileInputStream fis = new FileInputStream(file); + byte[] buff = new byte[1024]; + try { + int read; + while((read = fis.read(buff,0,1024))>=0) { + os.write(buff,0,read); + } + } finally { + fis.close(); + } + } + + } + private static class CachedContent extends Content { + private byte[] data; + private int end; + private char[] cdata; + + public CachedContent(File f) throws IOException { + // Read and Cache + ByteBuffer bb = ByteBuffer.allocate((int)f.length()); + FileInputStream fis = new FileInputStream(f); + try { + fis.getChannel().read(bb); + } finally { + fis.close(); + } + + data = bb.array(); + end = bb.position(); + cdata=null; + } + + public String toString() { + return data.toString(); + } + + public void write(Writer writer) throws IOException { + synchronized(this) { + // do the String Transformation once, and only if actually used + if(cdata==null) { + cdata = new char[end]; + new String(data).getChars(0, end, cdata, 0); + } + } + writer.write(cdata,0,end); + } + public void write(OutputStream os) throws IOException { + os.write(data,0,end); + } + + } + + public void setEnv(LogTarget env) { + logT = env; + } + + /** + * Cleanup thread to remove older items if max Cache is reached. + * + */ + private static class Cleanup extends TimerTask { + private int maxSize; + private NavigableMap content; + + public Cleanup(NavigableMap content, int size) { + maxSize = size; + this.content = content; + } + + private class Comp implements Comparable { + public Map.Entry entry; + + public Comp(Map.Entry en) { + entry = en; + } + + @Override + public int compareTo(Comp o) { + return (int)(entry.getValue().access-o.entry.getValue().access); + } + + } + @SuppressWarnings("unchecked") + @Override + public void run() { + int size = content.size(); + if(size>maxSize) { + ArrayList scont = new ArrayList(size); + Object[] entries = content.entrySet().toArray(); + for(int i=0;i)entries[i])); + } + Collections.sort(scont); + int end = size - ((maxSize/4)*3); // reduce to 3/4 of max size + System.out.println("------ Cleanup Cycle ------ " + new Date().toString() + " -------"); + for(int i=0;i entry = scont.get(i).entry; + content.remove(entry.getKey()); + System.out.println("removed Cache Item " + entry.getKey() + "/" + new Date(entry.getValue().access).toString()); + } + for(int i=end;i entry = scont.get(i).entry; + System.out.println("remaining Cache Item " + entry.getKey() + "/" + new Date(entry.getValue().access).toString()); + } + } + } + } +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java b/authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java new file mode 100644 index 00000000..01ecf38d --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/CodeSetter.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.att.inno.env.Trans; + +// Package on purpose. only want between RServlet and Routes +class CodeSetter { + private HttpCode code; + private TRANS trans; + private HttpServletRequest req; + private HttpServletResponse resp; + public CodeSetter(TRANS trans, HttpServletRequest req, HttpServletResponse resp) { + this.trans = trans; + this.req = req; + this.resp = resp; + + } + public boolean matches(Route route) throws IOException, ServletException { + // Find best Code in Route based on "Accepts (Get) or Content-Type" (if exists) + return (code = route.getCode(trans, req, resp))!=null; + } + + public HttpCode code() { + return code; + } +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Content.java b/authz-core/src/main/java/com/att/cssa/rserv/Content.java new file mode 100644 index 00000000..fd6c8a58 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/Content.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.util.List; + +import com.att.inno.env.Trans; + + + +/** + * A Class to hold Service "ContentTypes", and to match incoming "Accept" types from HTTP. + * + * This is a multi-use class built to use the same Parser for ContentTypes and Accept. + * + * Thus, you would create and use "Content.Type" within your service, and use it to match + * Accept Strings. What is returned is an Integer (for faster processing), which can be + * used in a switch statement to act on match different Actions. The server should + * know which behaviors match. + * + * "bestMatch" returns an integer for the best match, or -1 if no matches. + * + * + */ +public abstract class Content { + public static final String Q = "q"; + protected abstract Pair,List>>> types(HttpCode code, String str); + protected abstract boolean props(Pair,List>>> type, String tag, String value); + + /** + * Parse a Content-Type/Accept. As found, call "types" and "props", which do different + * things depending on if it's a Content-Type or Accepts. + * + * For Content-Type, it builds a tree suitable for Comparison + * For Accepts, it compares against the tree, and builds an acceptable type list + * + * Since this parse code is used for every incoming HTTP transaction, I have removed the implementation + * that uses String.split, and replaced with integers evaluating the Byte array. This results + * in only the necessary strings created, resulting in 1/3 better speed, and less + * Garbage collection. + * + * @param trans + * @param code + * @param cntnt + * @return + */ + protected boolean parse(HttpCode code, String cntnt) { + byte bytes[] = cntnt.getBytes(); + boolean contType=false,contProp=true; + int cis,cie=-1,cend; + int sis,sie,send; + do { + cis = cie+1; + cie = cntnt.indexOf(',',cis); + cend = cie<0?bytes.length:cie; + // Start SEMIS + sie=cis-1; + Pair, List>>> me = null; + do { + sis = sie+1; + sie = cntnt.indexOf(';',sis); + send = sie>cend || sie<0?cend:sie; + if(me==null) { + String semi = new String(bytes,sis,send-sis); + // trans.checkpoint(semi); + // Look at first entity within comma group + // Is this an acceptable Type? + me=types(code, semi); + if(me==null) { + sie=-1; // skip the rest of the processing... not a type + } else { + contType=true; + } + } else { // We've looped past the first Semi, now process as properties + // If there are additional elements (more entities within Semi Colons) + // apply Propertys + int eq = cntnt.indexOf('=',sis); + if(eq>sis && eq + * @param + */ +public abstract class HttpCode { + protected CONTEXT context; + private String desc; + protected String [] roles; + private boolean all; + + // Package by design... Set by Route when linked + Match match; + + public HttpCode(CONTEXT context, String description, String ... roles) { + this.context = context; + desc = description; + + // Evaluate for "*" once... + all = false; + for(String srole : roles) { + if("*".equals(srole)) { + all = true; + break; + } + } + this.roles = all?null:roles; + } + + public abstract void handle(TRANS trans, HttpServletRequest req, HttpServletResponse resp) throws Exception; + + public String desc() { + return desc; + } + + /** + * Get the variable element out of the Path Parameter, as set by initial Code + * + * @param req + * @param key + * @return + */ + public String pathParam(HttpServletRequest req, String key) { + return match.param(req.getPathInfo(), key); + } + + // Note: get Query Params from Request + + /** + * Check for Authorization when set. + * + * If no Roles set, then accepts all users + * + * @param req + * @return + */ + public boolean isAuthorized(HttpServletRequest req) { + if(all)return true; + if(roles!=null) { + for(String srole : roles) { + if(req.isUserInRole(srole)) return true; + } + } + return false; + } + + public boolean no_cache() { + return false; + } + + public String toString() { + return desc; + } +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java b/authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java new file mode 100644 index 00000000..c247aad0 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/HttpMethods.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +public enum HttpMethods { + POST, + GET, + PUT, + DELETE +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Match.java b/authz-core/src/main/java/com/att/cssa/rserv/Match.java new file mode 100644 index 00000000..1f5a60f5 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/Match.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * This path matching algorithm avoids using split strings during the critical transactional run-time. By pre-analyzing the + * content at "set Param" time, and storing data in an array-index model which presumably is done once and at the beginning, + * we can match in much less time when it actually counts. + * + * + */ +public class Match { + private Map params; + private byte[] values[]; + private Integer vars[]; + private boolean wildcard; + + + /* + * These two methods are pairs of searching performance for variables Spark Style. + * setParams evaluates the target path, and sets a HashMap that will return an Integer. + * the Keys are both :key and key so that there will be no string operations during + * a transaction + * + * For the Integer, if the High Order is 0, then it is just one value. If High Order >0, then it is + * a multi-field option, i.e. ending with a wild-card. + */ + public Match(String path) { + // IF DEBUG: System.out.print("\n[" + path + "]"); + params = new HashMap(); + if(path!=null) { + String[] pa = path.split("/"); + values = new byte[pa.length][]; + vars = new Integer[pa.length]; + + int val = 0; + String key; + for(int i=0;i1) { + /* remove * from value */ + int newlength = values[i].length-1; + byte[] real = new byte[newlength]; + System.arraycopy(values[i],0,real,0,newlength); + values[i]=real; + } else { + vars[i]=0; // this is actually a variable, if it only contains a "*" + } + } + // vars[i]=null; + } + } + } + } + + /* + * This is the second of the param evaluation functions. First, we look up to see if there is + * any reference by key in the params Map created by the above. + * + * The resulting Integer, if not null, is split high/low order into start and end. + * We evaluate the string for '/', rather than splitting into String[] to avoid the time/mem needed + * We traverse to the proper field number for slash, evaluate the end (whether wild card or no), + * and return the substring. + * + * The result is something less than .003 milliseconds per evaluation + * + */ + public String param(String path,String key) { + Integer val = params.get(key); // :key or key + if(val!=null) { + int start = val & 0xFFFF; + int end = (val >> 16) & 0xFFFF; + int idx = -1; + int i; + for(i=0;i0?(pabytes[0]=='/'):false; + // IF DEBUG: System.out.println("\n -- " + path + " --"); + for(int i=0;rv && i=lastField) { // checking here allows there to be a non-functional ending / + rv = false; + break; + } + if(values[field]==null) { // it's a variable, just look for /s + if(wildcard && field==lastField-1) return true;// we've made it this far. We accept all remaining characters + Integer val = vars[field]; + int start = val & 0xFFFF; + int end = (val >> 16) & 0xFFFF; + if(end==0)end=start+1; + int k = i; + for(int j=start; ji)i=k-1; // if we've incremented, have to accommodate the outer for loop incrementing as well + fieldMatched = false; // reset + fieldIdx = 0; + } else { + // IF DEBUG: System.out.print((char)pabytes[i]); + if(pabytes[i]=='/') { // end of field, eval if Field is matched + // if double slash, check if supposed to be empty + if(fieldIdx==0 && values[field].length==0) { + fieldMatched = true; + } + rv = fieldMatched && ++field getParamNames() { + return params.keySet(); + } +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Pair.java b/authz-core/src/main/java/com/att/cssa/rserv/Pair.java new file mode 100644 index 00000000..7d3b88c6 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/Pair.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +/** + * A pair of generic Objects. + * + * @param + * @param + */ +public class Pair { + public X x; + public Y y; + + public Pair(X x, Y y) { + this.x = x; + this.y = y; + } + + public String toString() { + return "X: " + x.toString() + "-->" + y.toString(); + } +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/RServlet.java b/authz-core/src/main/java/com/att/cssa/rserv/RServlet.java new file mode 100644 index 00000000..ed116e64 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/RServlet.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.att.inno.env.Env; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans; + +public abstract class RServlet implements Servlet { + private Routes routes = new Routes(); + + private ServletConfig config; + + @Override + public void init(ServletConfig config) throws ServletException { + this.config = config; + } + + @Override + public ServletConfig getServletConfig() { + return config; + } + + public void route(Env env, HttpMethods meth, String path, HttpCode code, String ... moreTypes) { + Route r = routes.findOrCreate(meth,path); + r.add(code,moreTypes); + env.init().log(r.report(code),code); + } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + HttpServletRequest request = (HttpServletRequest)req; + HttpServletResponse response = (HttpServletResponse)res; + + @SuppressWarnings("unchecked") + TRANS trans = (TRANS)req.getAttribute(TransFilter.TRANS_TAG); + if(trans==null) { + response.setStatus(404); // Not Found, because it didn't go through TransFilter + return; + } + + Route route; + HttpCode code=null; + String ct = req.getContentType(); + TimeTaken tt = trans.start("Resolve to Code", Env.SUB); + try { + // routes have multiple code sets. This object picks the best code set + // based on Accept or Content-Type + CodeSetter codesetter = new CodeSetter(trans,request,response); + // Find declared route + route = routes.derive(request, codesetter); + if(route==null) { + String method = request.getMethod(); + trans.checkpoint("No Route matches "+ method + ' ' + request.getPathInfo()); + response.setStatus(404); // Not Found + } else { + // Find best Code in Route based on "Accepts (Get) or Content-Type" (if exists) + code = codesetter.code();// route.getCode(trans, request, response); + } + } finally { + tt.done(); + } + + if(route!=null && code!=null) { + StringBuilder sb = new StringBuilder(72); + sb.append(route.auditText); + sb.append(','); + sb.append(code.desc()); + if(ct!=null) { + sb.append(", ContentType: "); + sb.append(ct); + } + tt = trans.start(sb.toString(),Env.SUB); + try { + /*obj = */ + code.handle(trans, request, response); + response.flushBuffer(); + } catch (ServletException e) { + trans.error().log(e); + throw e; + } catch (Exception e) { + trans.error().log(e,request.getMethod(),request.getPathInfo()); + throw new ServletException(e); + } finally { + tt.done(); + } + } + } + + @Override + public String getServletInfo() { + return "RServlet for Jetty"; + } + + @Override + public void destroy() { + } + + public String applicationJSON(Class cls, String version) { + StringBuilder sb = new StringBuilder(); + sb.append("application/"); + sb.append(cls.getSimpleName()); + sb.append("+json"); + sb.append(";charset=utf-8"); + sb.append(";version="); + sb.append(version); + return sb.toString(); + } + + public String applicationXML(Class cls, String version) { + StringBuilder sb = new StringBuilder(); + sb.append("application/"); + sb.append(cls.getSimpleName()); + sb.append("+xml"); + sb.append(";charset=utf-8"); + sb.append(";version="); + sb.append(version); + return sb.toString(); + } + + public List routeReport() { + return routes.routeReport(); + } +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Route.java b/authz-core/src/main/java/com/att/cssa/rserv/Route.java new file mode 100644 index 00000000..0a8cffe0 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/Route.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.att.inno.env.Env; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans; + +public class Route { + public final String auditText; + public final HttpMethods meth; + public final String path; + + private Match match; + // package on purpose + private final TypedCode content; + private final boolean isGet; + + public Route(HttpMethods meth, String path) { + this.path = path; + auditText = meth.name() + ' ' + path; + this.meth = meth; // Note: Using Spark def for now. + isGet = meth.compareTo(HttpMethods.GET) == 0; + match = new Match(path); + content = new TypedCode(); + } + + public void add(HttpCode code, String ... others) { + code.match = match; + content.add(code, others); + } + +// public void add(HttpCode code, Class cls, String version, String ... others) { +// code.match = match; +// content.add(code, cls, version, others); +// } +// + public HttpCode getCode(TRANS trans, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { + // Type is associated with Accept for GET (since it is what is being returned + // We associate the rest with ContentType. + // FYI, thought about this a long time before implementing this way. + String compare; +// String special[]; // todo, expose Charset (in special) to outside + if(isGet) { + compare = req.getHeader("Accept"); // Accept is used for read, as we want to agree on what caller is ready to handle + } else { + compare = req.getContentType(); // Content type used to declare what data is being created, updated or deleted (might be used for key) + } + + Pair, List>>> hl = content.prep(trans, compare); + if(hl==null) { + resp.setStatus(406); // NOT_ACCEPTABLE + } else { + if(isGet) { // Set Content Type to expected content + if("*".equals(hl.x) || "*/*".equals(hl.x)) {// if wild-card, then choose first kind of type + resp.setContentType(content.first()); + } else { + resp.setContentType(hl.x); + } + } + return hl.y.x; + } + return null; + } + + public Route matches(String method, String path) { + return meth.name().equalsIgnoreCase(method) && match.match(path)?this:null; + } + + public TimeTaken start(Trans trans, String auditText, HttpCode code, String type) { + StringBuilder sb = new StringBuilder(auditText); + sb.append(", "); + sb.append(code.desc()); + sb.append(", Content: "); + sb.append(type); + return trans.start(sb.toString(), Env.SUB); + } + + // Package on purpose.. for "find/Create" routes only + boolean resolvesTo(HttpMethods hm, String p) { + return(path.equals(p) && hm.equals(meth)); + } + + public String toString() { + return auditText + ' ' + content; + } + + public String report(HttpCode code) { + StringBuilder sb = new StringBuilder(); + sb.append(auditText); + sb.append(' '); + content.relatedTo(code, sb); + return sb.toString(); + } + + public RouteReport api() { + RouteReport tr = new RouteReport(); + tr.meth = meth; + tr.path = path; + content.api(tr); + return tr; + } + + + /** + * contentRelatedTo (For reporting) list routes that will end up at a specific Code + * @return + */ + public String contentRelatedTo(HttpCode code) { + StringBuilder sb = new StringBuilder(path); + sb.append(' '); + content.relatedTo(code, sb); + return sb.toString(); + } +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java b/authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java new file mode 100644 index 00000000..6dee8d32 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/RouteReport.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.util.ArrayList; +import java.util.List; + +public class RouteReport { + public HttpMethods meth; + public String path; + public String desc; + public final List contextTypes = new ArrayList(); + +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Routes.java b/authz-core/src/main/java/com/att/cssa/rserv/Routes.java new file mode 100644 index 00000000..c2d7fb82 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/Routes.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import com.att.inno.env.Trans; + + +public class Routes { + // Since this must be very, very fast, and only needs one creation, we'll use just an array. + private Route[] routes; + private int end; + + + @SuppressWarnings("unchecked") + public Routes() { + routes = new Route[10]; + end = 0; + } + + // This method for setup of Routes only... + // Package on purpose + synchronized Route findOrCreate(HttpMethods meth, String path) { + Route rv = null; + for(int i=0;i=routes.length) { + @SuppressWarnings("unchecked") + Route[] temp = new Route[end+10]; + System.arraycopy(routes, 0, temp, 0, routes.length); + routes = temp; + } + + routes[end++]=rv=new Route(meth,path); + } + return rv; + } + + public Route derive(HttpServletRequest req, CodeSetter codeSetter) throws IOException, ServletException { + Route rv = null; + String path = req.getPathInfo(); + String meth = req.getMethod(); + //TODO a TREE would be better + for(int i=0;rv==null && i routeReport() { + ArrayList ltr = new ArrayList(); + for(int i=0;i implements Filter { + public static final String TRANS_TAG = "__TRANS__"; + + private CadiHTTPManip cadi; + + public TransFilter(Access access, Connector con, TrustChecker tc, Object ... additionalTafLurs) throws CadiException { + cadi = new CadiHTTPManip(access, con, tc, additionalTafLurs); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + protected Lur getLur() { + return cadi.getLur(); + } + + protected abstract TRANS newTrans(); + protected abstract TimeTaken start(TRANS trans, ServletRequest request); + protected abstract void authenticated(TRANS trans, Principal p); + protected abstract void tallyHo(TRANS trans); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + TRANS trans = newTrans(); + + TimeTaken overall = start(trans,request); + try { + request.setAttribute(TRANS_TAG, trans); + + HttpServletRequest req = (HttpServletRequest)request; + HttpServletResponse res = (HttpServletResponse)response; + + TimeTaken security = trans.start("CADI Security", Env.SUB); +// TimeTaken ttvalid; + TafResp resp; + RESP r; + CadiWrap cw = null; + try { + resp = cadi.validate(req,res); + switch(r=resp.isAuthenticated()) { + case IS_AUTHENTICATED: + cw = new CadiWrap(req,resp,cadi.getLur()); + authenticated(trans, cw.getUserPrincipal()); + break; + default: + break; + } + } finally { + security.done(); + } + + if(r==RESP.IS_AUTHENTICATED) { + trans.checkpoint(resp.desc()); + chain.doFilter(cw, response); + } else { + //TODO this is a good place to check if too many checks recently + // Would need Cached Counter objects that are cleaned up on + // use + trans.checkpoint(resp.desc(),Env.ALWAYS); + if(resp.isFailedAttempt()) + trans.audit().log(resp.desc()); + } + } catch(Exception e) { + trans.error().log(e); + trans.checkpoint("Error: " + e.getClass().getSimpleName() + ": " + e.getMessage()); + throw new ServletException(e); + } finally { + overall.done(); + tallyHo(trans); + } + } + + @Override + public void destroy() { + }; +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java b/authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java new file mode 100644 index 00000000..1ef5456a --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/TransOnlyFilter.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.io.IOException; +import java.security.Principal; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import com.att.inno.env.TimeTaken; +import com.att.inno.env.TransStore; + +/** + * Create a new Transaction Object for each and every incoming Transaction + * + * Attach to Request. User "FilterHolder" mechanism to retain single instance. + * + * TransFilter includes CADIFilter as part of the package, so that it can + * set User Data, etc, as necessary. + * + * + */ +public abstract class TransOnlyFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + + + protected abstract TRANS newTrans(); + protected abstract TimeTaken start(TRANS trans, ServletRequest request); + protected abstract void authenticated(TRANS trans, Principal p); + protected abstract void tallyHo(TRANS trans); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + TRANS trans = newTrans(); + + TimeTaken overall = start(trans,request); + try { + request.setAttribute(TransFilter.TRANS_TAG, trans); + chain.doFilter(request, response); + } finally { + overall.done(); + } + tallyHo(trans); + } + + @Override + public void destroy() { + }; +} diff --git a/authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java b/authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java new file mode 100644 index 00000000..6fd7049a --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/TypedCode.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.servlet.ServletException; + +import com.att.inno.env.Env; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans; + + +/** + * TypedCode organizes implementation code based on the Type and Version of code it works with so that it can + * be located quickly at runtime based on the "Accept" HTTP Header. + * + * FYI: For those in the future wondering why I would create a specialized set of "Pair" for the data content: + * 1) TypeCode is used in Route, and this code is used for every transaction... it needs to be blazingly fast + * 2) The actual number of objects accessed is quite small and built at startup. Arrays are best + * 3) I needed a small, well defined tree where each level is a different Type. Using a "Pair" Generic definitions, + * I created type-safety at each level, which you can't get from a TreeSet, etc. + * 4) Chaining through the Network is simply object dereferencing, which is as fast as Java can go. + * 5) The drawback is that in your code is that all the variables are named "x" and "y", which can be a bit hard to + * read both in code, and in the debugger. However, TypeSafety allows your IDE (Eclipse) to help you make the + * choices. Also, make sure you have a good "toString()" method on each object so you can see what's happening + * in the IDE Debugger. + * + * Empirically, this method of obtaining routes proved to be much faster than the HashSet implementations available in otherwise + * competent Open Source. + * + * @param + */ +public class TypedCode extends Content { + private List,List>>>> types; + + public TypedCode() { + types = new ArrayList,List>>>>(); + } + + /** + * Construct Typed Code based on ContentType parameters passed in + * + * @param code + * @param others + * @return + */ + public TypedCode add(HttpCode code, String ... others) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String str : others) { + if(first) { + first = false; + } else { + sb.append(','); + } + sb.append(str); + } + parse(code, sb.toString()); + + return this; + } + + @Override + protected Pair, List>>> types(HttpCode code, String str) { + Pair,List>>> type = null; + ArrayList> props = new ArrayList>(); + // Want Q percentage is to be first in the array everytime. If not listed, 1.0 is default + props.add(new Pair(Q,1f)); + Pair, List>> cl = new Pair, List>>(code, props); +// // breakup "plus" stuff, i.e. application/xaml+xml +// int plus = str.indexOf('+'); +// if(plus<0) { + type = new Pair,List>>>(str, cl); + types.add(type); + return type; +// } else { +// int prev = str.indexOf('/')+1; +// String first = str.substring(0,prev); +// String nstr; +// while(prev!=0) { +// nstr = first + (plus>-1?str.substring(prev,plus):str.substring(prev)); +// type = new Pair,List>>>(nstr, cl); +// types.add(type); +// prev = plus+1; +// plus = str.indexOf('+',prev); +// } +// return type; +// } + } + + @Override + protected boolean props(Pair, List>>> type, String tag, String value) { + if(tag.equals(Q)) { // reset the Q value (first in array) + boolean rv = true; + try { + type.y.y.get(0).y=Float.parseFloat(value); + return rv; + } catch (NumberFormatException e) { + rv=false; // Note: this awkward syntax forced by Sonar, which doesn't like doing nothing with Exception + // which is what should happen + } + } + return type.y.y.add(new Pair(tag,"version".equals(tag)?new Version(value):value)); + } + + public Pair, List>>> prep(TRANS trans, String compare) throws IOException, ServletException { + Pair, List>>> c,rv=null; + if(types.size()==1 && "".equals((c=types.get(0)).x)) { // if there are no checks for type, skip + rv = c; + } else { + if(compare==null || compare.length()==0) { + rv = types.get(0); // first code is used + } else { + Acceptor acc = new Acceptor(types); + boolean accepted; + TimeTaken tt = trans.start(compare, Env.SUB); + try { + accepted = acc.parse(null, compare); + } finally { + tt.done(); + } + if(accepted) { + switch(acc.acceptable.size()) { + case 0: +// // TODO best Status Code? +// resp.setStatus(HttpStatus.NOT_ACCEPTABLE_406); + break; + case 1: + rv = acc.acceptable.get(0); + break; + default: // compare Q values to get Best Match + float bestQ = -1.0f; + Pair, List>>> bestT = null; + for(Pair, List>>> type : acc.acceptable) { + Float f = (Float)type.y.y.get(0).y; // first property is always Q + if(f>bestQ) { + bestQ=f; + bestT = type; + } + } + if(bestT!=null) { + // When it is a GET, the matched type is what is returned, so set ContentType +// if(isGet)resp.setContentType(bestT.x); // set ContentType of Code +// rv = bestT.y.x; + rv = bestT; + } + } + } else { + trans.checkpoint("No Match found for Accept"); + } + } + } + return rv; + } + + /** + * Print on String Builder content related to specific Code + * + * This is for Reporting and Debugging purposes, so the content is not cached. + * + * If code is "null", then all content is matched + * + * @param code + * @return + */ + public StringBuilder relatedTo(HttpCode code, StringBuilder sb) { + boolean first = true; + for(Pair, List>>> pair : types) { + if(code==null || pair.y.x == code) { + if(first) { + first = false; + } else { + sb.append(','); + } + sb.append(pair.x); + for(Pair prop : pair.y.y) { + // Don't print "Q". it's there for internal use, but it is only meaningful for "Accepts" + if(!prop.x.equals(Q) || !prop.y.equals(1f) ) { + sb.append(';'); + sb.append(prop.x); + sb.append('='); + sb.append(prop.y); + } + } + } + } + return sb; + } + + public List> getContent(HttpCode code) { + for(Pair, List>>> pair : types) { + if(pair.y.x == code) { + return pair.y.y; + } + } + return null; + } + + public String toString() { + return relatedTo(null,new StringBuilder()).toString(); + } + + public void api(RouteReport tr) { + // Need to build up a map, because Prop entries can be in several places. + HashMap,StringBuilder> psb = new HashMap,StringBuilder>(); + StringBuilder temp; + tr.desc = null; + + // Read through Code/TypeCode trees for all accepted Typecodes + for(Pair, List>>> tc : types) { + // If new, then it's new Code set, create prefix content + if((temp=psb.get(tc.y.x))==null) { + psb.put(tc.y.x,temp=new StringBuilder()); + if(tr.desc==null) { + tr.desc = tc.y.x.desc(); + } + } else { + temp.append(','); + } + temp.append(tc.x); + + // add all properties + for(Pair props : tc.y.y) { + temp.append(';'); + temp.append(props.x); + temp.append('='); + temp.append(props.y); + } + } + // Gather all ContentType possibilities for the same code together + + for(StringBuilder sb : psb.values()) { + tr.contextTypes.add(sb.toString()); + } + } + + public String first() { + if(types.size()>0) { + return types.get(0).x; + } + return null; + } + + } diff --git a/authz-core/src/main/java/com/att/cssa/rserv/Version.java b/authz-core/src/main/java/com/att/cssa/rserv/Version.java new file mode 100644 index 00000000..e24c48eb --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/Version.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cssa.rserv; + + +/** + * Analyze and hold Version information for Code + * + * + */ +public class Version { + private Object[] parts; + + public Version(String v) { + String sparts[] = v.split("\\."); + parts = new Object[sparts.length]; + System.arraycopy(sparts, 0, parts, 0, sparts.length); + if(parts.length>1) { // has at least a minor + try { + parts[1]=Integer.decode(sparts[1]); // minor elements need to be converted to Integer for comparison + } catch (NumberFormatException e) { + // it's ok, leave it as a string + parts[1]=sparts[1]; // This useless piece of code forced by Sonar which calls empty Exceptions "Blockers". + } + } + } + + public boolean equals(Object obj) { + if(obj instanceof Version) { + Version ver = (Version)obj; + int length = Math.min(parts.length, ver.parts.length); + for(int i=0;i