diff options
Diffstat (limited to 'authz-core/src/main/java/com/att')
36 files changed, 5455 insertions, 0 deletions
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;i<times.length;++i) {
+ if(times[i]>0) {
+ if(prev>0) {
+ ++count;
+ pot += times[i]-prev;
+ }
+ prev = times[i];
+ }
+ }
+ for(int i=0;i<idx;++i) {
+ if(times[i]>0) {
+ 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<Object, Object> 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<AuthzTrans> {
+ 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<serviceMetric.buckets.length;++i) {
+ serviceMetric.buckets[i]+=m.buckets[i];
+ }
+
+ // Log current info
+ sb.append(" Total: ");
+ sb.append(m.total);
+ sb.append(" Remote: ");
+ sb.append(m.buckets[0]);
+ sb.append(" JSON: ");
+ sb.append(m.buckets[1]);
+ trans.info().log(sb);
+ } else {
+ // IMPORTANT!!! if you add more entries here, change "BUCKETSIZE"!!!
+ StringBuilder content = new StringBuilder();
+ Metric m = trans.auditTrail(1, content, Env.REMOTE,Env.JSON);
+ // Add current Metrics to total metrics
+ serviceMetric.total+= m.total;
+ for(int i=0;i<serviceMetric.buckets.length;++i) {
+ serviceMetric.buckets[i]+=m.buckets[i];
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("user=");
+ Principal p = trans.getUserPrincipal();
+ if(p==null) {
+ sb.append("n/a");
+ } else {
+ sb.append(p.getName());
+ if(p instanceof TrustPrincipal) {
+ sb.append('(');
+ sb.append(((TrustPrincipal)p).getOrigName());
+ sb.append(')');
+ } else {
+ sb.append('[');
+ if(p instanceof X509Principal) {
+ sb.append("x509");
+ } else if(p instanceof BasicPrincipal) {
+ sb.append("BAth");
+ } else {
+ sb.append(p.getClass().getSimpleName());
+ }
+ sb.append(']');
+ }
+ }
+ sb.append(",ip=");
+ sb.append(trans.ip());
+ sb.append(",port=");
+ sb.append(trans.port());
+ sb.append(",ms=");
+ sb.append(m.total);
+ sb.append(",meth=");
+ sb.append(trans.meth());
+ sb.append(",path=");
+ sb.append(trans.path());
+
+ Long tsi;
+ if((tsi=trans.get(transIDslot, null))!=null) {
+ sb.append(",traceID=");
+ sb.append(Long.toHexString(tsi));
+ }
+
+ if(content.length()>0) {
+ sb.append(",msg=\"");
+ sb.append(content);
+ sb.append('"');
+ }
+
+ trans.warn().log(sb);
+ }
+ }
+
+}
diff --git a/authz-core/src/main/java/com/att/authz/env/AuthzTransImpl.java b/authz-core/src/main/java/com/att/authz/env/AuthzTransImpl.java new file mode 100644 index 00000000..77f94a41 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/env/AuthzTransImpl.java @@ -0,0 +1,198 @@ +/*******************************************************************************
+ * ============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.authz.org.OrganizationFactory;
+import com.att.cadi.Lur;
+import com.att.cadi.Permission;
+import com.att.inno.env.LogTarget;
+import com.att.inno.env.impl.BasicTrans;
+
+public class AuthzTransImpl extends BasicTrans implements AuthzTrans {
+ private static final String TRUE = "true";
+ private Principal user;
+ private String ip,agent,meth,path;
+ private int port;
+ private Lur lur;
+ private Organization org;
+ private String force;
+ private boolean futureRequested;
+
+ public AuthzTransImpl(AuthzEnv env) {
+ super(env);
+ ip="n/a";
+ org=null;
+ }
+
+ /**
+ * @see com.att.authz.env.AuthTrans#set(javax.servlet.http.HttpServletRequest)
+ */
+ @Override
+ public AuthzTrans set(HttpServletRequest req) {
+ user = req.getUserPrincipal();
+ ip = req.getRemoteAddr();
+ port = req.getRemotePort();
+ agent = req.getHeader("User-Agent");
+ meth = req.getMethod();
+ path = req.getPathInfo();
+ force = req.getParameter("force");
+ futureRequested = TRUE.equalsIgnoreCase(req.getParameter("request"));
+ org=null;
+ return this;
+ }
+
+ @Override
+ public void setUser(Principal p) {
+ user = p;
+ }
+
+ /**
+ * @see com.att.authz.env.AuthTrans#user()
+ */
+ @Override
+ public String user() {
+ return user==null?"n/a":user.getName();
+ }
+
+ /**
+ * @see com.att.authz.env.AuthTrans#getUserPrincipal()
+ */
+ @Override
+ public Principal getUserPrincipal() {
+ return user;
+ }
+
+ /**
+ * @see com.att.authz.env.AuthTrans#ip()
+ */
+ @Override
+ public String ip() {
+ return ip;
+ }
+
+ /**
+ * @see com.att.authz.env.AuthTrans#port()
+ */
+ @Override
+ public int port() {
+ return port;
+ }
+
+
+ /* (non-Javadoc)
+ * @see com.att.authz.env.AuthzTrans#meth()
+ */
+ @Override
+ public String meth() {
+ return meth;
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.env.AuthzTrans#path()
+ */
+ @Override
+ public String path() {
+ return path;
+ }
+
+ /**
+ * @see com.att.authz.env.AuthTrans#agent()
+ */
+ @Override
+ public String agent() {
+ return agent;
+ }
+
+ @Override
+ public AuthzEnv env() {
+ return (AuthzEnv)delegate;
+ }
+
+ @Override
+ public boolean forceRequested() {
+ return TRUE.equalsIgnoreCase(force);
+ }
+
+ public void forceRequested(boolean force) {
+ this.force = force?TRUE:"false";
+ }
+
+ @Override
+ public boolean moveRequested() {
+ return "move".equalsIgnoreCase(force);
+ }
+
+ @Override
+ public boolean futureRequested() {
+ return futureRequested;
+ }
+
+
+ @Override
+ public void setLur(Lur lur) {
+ this.lur = lur;
+ }
+
+ @Override
+ public boolean fish(Permission p) {
+ if(lur!=null) {
+ return lur.fish(user, p);
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.env.AuthzTrans#org()
+ */
+ @Override
+ public Organization org() {
+ if(org==null) {
+ try {
+ if((org = OrganizationFactory.obtain(env(), user()))==null) {
+ org = Organization.NULL;
+ }
+ } catch (Exception e) {
+ org = Organization.NULL;
+ }
+ }
+ return org;
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.env.AuthzTrans#logAuditTrailOnly(com.att.inno.env.LogTarget)
+ */
+ @Override
+ public void logAuditTrail(LogTarget lt) {
+ if(lt.isLoggable()) {
+ StringBuilder sb = new StringBuilder();
+ auditTrail(1, sb);
+ lt.log(sb);
+ }
+ }
+}
diff --git a/authz-core/src/main/java/com/att/authz/env/AuthzTransOnlyFilter.java b/authz-core/src/main/java/com/att/authz/env/AuthzTransOnlyFilter.java new file mode 100644 index 00000000..ecc95774 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/env/AuthzTransOnlyFilter.java @@ -0,0 +1,89 @@ +/*******************************************************************************
+ * ============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.cssa.rserv.TransOnlyFilter;
+import com.att.inno.env.Env;
+import com.att.inno.env.TimeTaken;
+import com.att.inno.env.Trans.Metric;
+
+public class AuthzTransOnlyFilter extends TransOnlyFilter<AuthzTrans> {
+ 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<serviceMetric.buckets.length;++i) {
+ serviceMetric.buckets[i]+=m.buckets[i];
+ }
+ // Log current info
+ sb.append(" Total: ");
+ sb.append(m.total);
+ sb.append(" Remote: ");
+ sb.append(m.buckets[0]);
+ sb.append(" JSON: ");
+ sb.append(m.buckets[1]);
+ trans.info().log(sb);
+ }
+
+}
diff --git a/authz-core/src/main/java/com/att/authz/env/NullTrans.java b/authz-core/src/main/java/com/att/authz/env/NullTrans.java new file mode 100644 index 00000000..b65994b6 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/env/NullTrans.java @@ -0,0 +1,225 @@ +/*******************************************************************************
+ * ============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.Decryptor;
+import com.att.inno.env.Encryptor;
+import com.att.inno.env.LogTarget;
+import com.att.inno.env.Slot;
+import com.att.inno.env.StaticSlot;
+import com.att.inno.env.TimeTaken;
+
+/**
+ * A NULL implementation of AuthzTrans, for use in DirectAAF Taf/Lurs
+ */
+public class NullTrans implements AuthzTrans {
+ private static final AuthzTrans singleton = new NullTrans();
+
+ public static final AuthzTrans singleton() {
+ return singleton;
+ }
+
+ public void checkpoint(String text) {}
+ public void checkpoint(String text, int additionalFlag) {}
+ public Metric auditTrail(int indent, StringBuilder sb, int... flag) {return null;}
+ public LogTarget fatal() {
+ return LogTarget.NULL;
+ }
+
+ public LogTarget error() {
+ return LogTarget.NULL;
+ }
+
+ public LogTarget audit() {
+ return LogTarget.NULL;
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.env.Env#init()
+ */
+ @Override
+ public LogTarget init() {
+ return LogTarget.NULL;
+ }
+
+ public LogTarget warn() {
+ return LogTarget.NULL;
+ }
+
+ public LogTarget info() {
+ return LogTarget.NULL;
+ }
+
+ public LogTarget debug() {
+ return LogTarget.NULL;
+ }
+
+ public LogTarget trace() {
+ return LogTarget.NULL;
+ }
+
+ public TimeTaken start(String name, int flag) {
+ return new TimeTaken(name,flag) {
+ public void output(StringBuilder sb) {
+ sb.append(name);
+ sb.append(' ');
+ sb.append(millis());
+ sb.append("ms");
+ }
+ };
+ }
+
+ @Override
+ public String setProperty(String tag, String value) {
+ return value;
+ }
+
+ @Override
+ public String getProperty(String tag) {
+ return tag;
+ }
+
+ @Override
+ public String getProperty(String tag, String deflt) {
+ return deflt;
+ }
+
+ @Override
+ public Decryptor decryptor() {
+ return null;
+ }
+
+ @Override
+ public Encryptor encryptor() {
+ return null;
+ }
+ @Override
+ public AuthzTrans set(HttpServletRequest req) {
+ return null;
+ }
+
+ @Override
+ public String user() {
+ return null;
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return null;
+ }
+
+ @Override
+ public String ip() {
+ return null;
+ }
+
+ @Override
+ public int port() {
+ return 0;
+ }
+ @Override
+ public String meth() {
+ return null;
+ }
+
+ @Override
+ public String path() {
+ return null;
+ }
+
+ @Override
+ public void put(Slot slot, Object value) {
+ }
+ @Override
+ public <T> T get(Slot slot, T deflt) {
+ return null;
+ }
+ @Override
+ public <T> 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<RV> {
+ 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<R> Result<R> ok(R value) {
+ return new Result<R>(value,OK,SUCCESS,null);
+ }
+
+ /**
+ * Accept Arrays and mark as empty or not
+ * @param value
+ * @return
+ */
+ public static<R> Result<R[]> ok(R value[]) {
+ return new Result<R[]>(value,OK,SUCCESS,null).emptyList(value.length==0);
+ }
+
+ /**
+ * Accept Sets and mark as empty or not
+ * @param value
+ * @return
+ */
+ public static<R> Result<Set<R>> ok(Set<R> value) {
+ return new Result<Set<R>>(value,OK,SUCCESS,null).emptyList(value.size()==0);
+ }
+
+ /**
+ * Accept Lists and mark as empty or not
+ * @param value
+ * @return
+ */
+ public static<R> Result<List<R>> ok(List<R> value) {
+ return new Result<List<R>>(value,OK,SUCCESS,null).emptyList(value.size()==0);
+ }
+
+ /**
+ * Accept Collections and mark as empty or not
+ * @param value
+ * @return
+ */
+ public static<R> Result<Collection<R>> ok(Collection<R> value) {
+ return new Result<Collection<R>>(value,OK,SUCCESS,null).emptyList(value.size()==0);
+ }
+
+
+ /**
+ * Special Case for Void Type
+ * @return
+ */
+ public static Result<Void> ok() {
+ return new Result<Void>(null,OK,SUCCESS,null);
+ }
+
+ /**
+ * Create a Status (usually non OK, with a details statement
+ * @param value
+ * @param status
+ * @param details
+ * @return
+ */
+// public static<R> Result<R> err(int status, String details) {
+// return new Result<R>(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<R> Result<R> err(int status, String details, String ... variables) {
+ return new Result<R>(null,status,details,variables);
+ }
+
+ /**
+ * Create Error from status and Details of previous Result (and not data)
+ * @param pdr
+ * @return
+ */
+ public static<R> Result<R> err(Result<?> pdr) {
+ return new Result<R>(null,pdr.status,pdr.details,pdr.variables);
+ }
+
+ /**
+ * Create General Error from Exception
+ * @param e
+ * @return
+ */
+ public static<R> Result<R> err(Exception e) {
+ return new Result<R>(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<R> Result<R> create(R value, int status, String details, String ... vars) {
+ return new Result<R>(value,status,details,vars);
+ }
+
+ /**
+ * Create a Status from a previous status' result/details
+ * @param value
+ * @param status
+ * @param details
+ * @return
+ */
+ public static<R> Result<R> create(R value, Result<?> result) {
+ return new Result<R>(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<RV> 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<RV> 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<String> {
+ 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()) && begin<end) {
+ if(first) {
+ trans.warn().log("Waiting for",lockf.getCanonicalPath(),"to close");
+ first = false;
+ }
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ break;
+ }
+ begin = System.currentTimeMillis();
+ }
+ if(exists) {
+ throw new IOException(lockf.getCanonicalPath() + "exists. May not open Datafile");
+ }
+ data.open();
+ try {
+ ensureIdxGood(trans);
+ } catch (IOException e) {
+ data.close();
+ throw e;
+ }
+ ti.open();
+ opened = true;
+
+ } finally {
+ tt.done();
+ }
+ if(!opened) {
+ throw new IOException("DataFile pair for " + name + " was not able to be opened in " + timeout + "ms");
+ }
+ }
+
+ private synchronized void ensureIdxGood(AuthzTrans trans) throws IOException {
+ if(!idxf.exists() || idxf.length()==0 || dataf.lastModified()>idxf.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<String> {
+ 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;++i) {
+ if(buff[i]=='\n') {
+ end = i;
+ next += i+1;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public IntBuffer getIntBuffer() {
+ return ByteBuffer.wrap(buff).asIntBuffer();
+ }
+
+
+
+ public String toString() {
+ return new String(buff,0,end);
+ }
+ public class Field {
+ char delim;
+ int idx;
+ ByteBuffer bb;
+
+ public Field(char delimiter) {
+ delim = delimiter;
+ idx = 0;
+ bb = null;
+ }
+
+ public Field reset() {
+ idx = 0;
+ return this;
+ }
+
+ public String next() {
+ if(idx>=end)return null;
+ int start = idx;
+ byte c=0;
+ int endStr = -1;
+ while(idx<end && idx<buff.length && (c=buff[idx])!=delim && c!='\n') { // for DOS
+ if(c=='\r')endStr=idx;
+ ++idx;
+ }
+
+ if(endStr<0) {
+ endStr=idx-start;
+ } else {
+ endStr=endStr-start;
+ }
+ ++idx;
+ return new String(buff,start,endStr);
+ }
+
+ public String at(int fieldOffset) {
+ int start;
+ byte c=0;
+ for(int count = idx = start = 0; idx<end && idx<buff.length; ++idx) {
+ if((c=buff[idx])==delim || c=='\n') {
+ if(count++ == fieldOffset) {
+ break;
+ }
+ start = idx+1;
+ }
+ }
+ return new String(buff,start,(idx-start-(c=='\r'?1:0)));
+ }
+
+ public String atToEnd(int fieldOffset) {
+ int start;
+ byte c=0;
+ for(int count = idx = start = 0; idx<end && idx<buff.length; ++idx) {
+ if((c=buff[idx])==delim || c=='\n') {
+ if(count++ == fieldOffset) {
+ break;
+ }
+ start = idx+1;
+ }
+ }
+
+ for(; idx<end && idx<buff.length && (c=buff[idx])!='\n'; ++idx) {
+ ++idx;
+ }
+ return new String(buff,start,(idx-start-((c=='\r' || 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(lhash<hash) {
+ min=curr+1;
+ } else if(lhash>hash) {
+ max=curr-1;
+ } else {
+ min=curr-40;
+ max=curr+40;
+ break;
+ }
+ }
+
+ List<Integer> entries = new ArrayList<Integer>();
+ 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<Idx> list = new LinkedList<Idx>(); // 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<skipLines;++i) {
+ t.nextLine();
+ }
+ tt2 = trans.start("Read", Env.SUB);
+ try {
+ while(t.nextLine()) {
+ list.add(new Idx(f.at(fieldOffset),t.pos()));
+ ++count;
+ }
+ } finally {
+ tt2.done();
+ }
+ trans.checkpoint(" Read " + count + " records");
+ tt2 = trans.start("Sort List", Env.SUB);
+ Collections.sort(list);
+ tt2.done();
+ tt2 = trans.start("Write Idx", Env.SUB);
+ try {
+ ByteBuffer bb = ByteBuffer.allocate(8*1024);
+ IntBuffer ib = bb.asIntBuffer();
+ for(Idx idx : list) {
+ if(!ib.hasRemaining()) {
+ fos.write(bb);
+ ib.clear();
+ bb.rewind();
+ }
+ ib.put(idx.hash);
+ ib.put(idx.pos);
+ }
+ bb.limit(4*ib.position());
+ fos.write(bb);
+ } finally {
+ tt2.done();
+ }
+ } finally {
+ fos.close();
+ raf.close();
+ }
+ }
+
+ public class Iter {
+ private int idx;
+ private Token t;
+ private long end;
+ private IntBuffer ib;
+
+
+ public Iter() {
+ try {
+ idx = 0;
+ end = dataFile.size();
+ t = dataFile.new Token(REC_SIZE);
+ ib = t.getIntBuffer();
+
+ } catch (IOException e) {
+ end = -1L;
+ }
+ }
+
+ public int next() {
+ t.pos(idx);
+ ib.clear();
+ ib.get();
+ int rec = ib.get();
+ idx += REC_SIZE;
+ return rec;
+ }
+
+ public boolean hasNext() {
+ return idx<end;
+ }
+ }
+
+ private static class Idx implements Comparable<Idx> {
+ 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<b?-1:0;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object o) {
+ if(o!=null && o instanceof Idx) {
+ return hash == ((Idx)o).hash;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hash;
+ }
+ }
+}
diff --git a/authz-core/src/main/java/com/att/authz/org/EmailWarnings.java b/authz-core/src/main/java/com/att/authz/org/EmailWarnings.java new file mode 100644 index 00000000..0f87eb21 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/org/EmailWarnings.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.authz.org;
+
+public interface EmailWarnings
+{
+ public long credExpirationWarning();
+ public long roleExpirationWarning();
+ public long credEmailInterval();
+ public long roleEmailInterval();
+ public long apprEmailInterval();
+ public long emailUrgentWarning();
+
+}
diff --git a/authz-core/src/main/java/com/att/authz/org/Executor.java b/authz-core/src/main/java/com/att/authz/org/Executor.java new file mode 100644 index 00000000..dcdf6462 --- /dev/null +++ b/authz-core/src/main/java/com/att/authz/org/Executor.java @@ -0,0 +1,36 @@ +/*******************************************************************************
+ * ============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 interface Executor {
+ // remove User from user/Role
+ // remove user from Admins
+ // if # of Owners > 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<String> 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<String> 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<String> toList, List<String> 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<Identity> getApprovers(AuthzTrans trans, String user) throws OrganizationException ;
+
+ /*
+ *
+ * @param user
+ * @param type
+ * @param users
+ * @return
+ public Response notifyRequest(AuthzTrans trans, String user, Approval type, List<User> 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<Identity> nullList = new ArrayList<Identity>();
+ private final Set<String> nullStringSet = new HashSet<String>();
+ private final Identity nullIdentity = new Identity() {
+ List<String> nullIdentity = new ArrayList<String>();
+ @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<String> 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<String> 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<String> toList, List<String> 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<Identity> 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<String,Organization> orgs = new ConcurrentHashMap<String,Organization>();
+ private static Slot orgSlot;
+
+ public static void setDefaultOrg(AuthzEnv env, String orgClass) throws APIException {
+ orgSlot = env.slot(ORG_SLOT);
+ try {
+ @SuppressWarnings("unchecked")
+ Class<Organization> cls = (Class<Organization>) Class.forName(orgClass);
+ Constructor<Organization> 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<Organization> cls = (Class<Organization>) Class.forName(orgClass);
+ Constructor<Organization> 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<AuthzTrans> {
+ 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> RET clientAsUser(Principal p,Retryable<RET> 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 <TRANS>
+ * @param <DATA>
+ */
+public class Cache<TRANS extends Trans, DATA> {
+ 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<String,Map<String,Dated>> cacheMap;
+
+ static {
+ cacheMap = new HashMap<String,Map<String,Dated>>();
+ }
+
+ /**
+ * 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 <T> Dated(T t) {
+ timestamp = new Date();
+ ArrayList<T> al = new ArrayList<T>(1);
+ al.add(t);
+ data = al;
+ }
+
+ public void touch() {
+ timestamp = new Date();
+ }
+ }
+
+ public static Map<String,Dated> obtain(String key) {
+ Map<String, Dated> m = cacheMap.get(key);
+ if(m==null) {
+ m = new ConcurrentHashMap<String, Dated>();
+ 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<String> 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<String>();
+ }
+
+ 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<String,Dated> map = cacheMap.get(name);
+ if(map!=null) for(Map.Entry<String,Dated> 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 <TRANS>
+ */
+class Acceptor<TRANS extends Trans> {
+ private List<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>> types;
+ List<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>> acceptable;
+
+ public Acceptor(List<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>> types) {
+ this.types = types;
+ acceptable = new ArrayList<Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>>>();
+ }
+
+ private boolean eval(HttpCode<TRANS,?> code, String str, List<String> props) {
+// int plus = str.indexOf('+');
+// if(plus<0) {
+ boolean ok = false;
+ boolean any = false;
+ for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : types) {
+ ok = true;
+ if(type.x.equals(str)) {
+ for(Iterator<String> 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<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> 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<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type, String tag, String value) {
+ boolean rv = false;
+ if(type.y!=null) {
+ for(Pair<String,Object> 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<TRANS, ?> code, String cntnt) {
+ byte bytes[] = cntnt.getBytes();
+
+ int cis,cie=-1,cend;
+ int sis,sie,send;
+ String name;
+ ArrayList<String> props = new ArrayList<String>();
+ 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(cis<bytes.length && Character.isSpaceChar(bytes[cis]))++cis;
+ cie = cntnt.indexOf(',',cis); // find comma end
+ cend = cie<0?bytes.length:cie; // If no comma, set comma end to full length, else cie
+ while(cend>cis && Character.isSpaceChar(bytes[cend-1]))--cend;
+ // Start SEMIS
+ sie=cis-1;
+ do {
+ sis = sie+1; // semi start is one after previous end
+ while(sis<bytes.length && Character.isSpaceChar(bytes[sis]))++sis;
+ sie = cntnt.indexOf(';',sis);
+ send = sie>cend || 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<send) {
+ props.add(new String(bytes,sis,eq-sis));
+ props.add(new String(bytes,eq+1,send-(eq+1)));
+ }
+ }
+ // End Property
+ } while(sie<=cend && sie>=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<TRANS extends Trans> extends HttpCode<TRANS, Void> {
+ public static void setEnv(Store store, String[] args) {
+ for(int i=0;i<args.length-1;i+=2) { // cover two parms required for each
+ if(CFA_WEB_DIR.equals(args[i])) {
+ store.put(store.staticSlot(CFA_WEB_DIR), args[i+1]);
+ } else if(CFA_CACHE_CHECK_INTERVAL.equals(args[i])) {
+ store.put(store.staticSlot(CFA_CACHE_CHECK_INTERVAL), Long.parseLong(args[i+1]));
+ } else if(CFA_MAX_SIZE.equals(args[i])) {
+ store.put(store.staticSlot(CFA_MAX_SIZE), Integer.parseInt(args[i+1]));
+ }
+ }
+ }
+
+ private static String MAX_AGE = "max-age=3600"; // 1 hour Caching
+ private final Map<String,String> typeMap;
+ private final NavigableMap<String,Content> content;
+ private final Set<String> 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<String,Content>(); // multi-thread changes possible
+
+ attachOnly = new HashSet<String>(); // short, unchanged
+
+ typeMap = new TreeMap<String,String>(); // 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<String, Content> content;
+
+ public Cleanup(NavigableMap<String, Content> content, int size) {
+ maxSize = size;
+ this.content = content;
+ }
+
+ private class Comp implements Comparable<Comp> {
+ public Map.Entry<String, Content> entry;
+
+ public Comp(Map.Entry<String, Content> 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<Comp> scont = new ArrayList<Comp>(size);
+ Object[] entries = content.entrySet().toArray();
+ for(int i=0;i<size;++i) {
+ scont.add(i, new Comp((Map.Entry<String,Content>)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<end;++i) {
+ Entry<String, Content> 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<size;++i) {
+ Entry<String, Content> 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<TRANS extends Trans> {
+ private HttpCode<TRANS,?> 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<TRANS> 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<TRANS,?> 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<TRANS extends Trans> {
+ public static final String Q = "q";
+ protected abstract Pair<String,Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>> types(HttpCode<TRANS,?> code, String str);
+ protected abstract boolean props(Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>> 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<TRANS,?> 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<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> 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<send) {
+ String tag = new String(bytes,sis,eq-sis);
+ String value = new String(bytes,eq+1,send-(eq+1));
+ // trans.checkpoint(" Prop " + tag + "=" + value);
+ boolean bool = props(me,tag,value);
+ if(!bool) {
+ contProp=false;
+ }
+ }
+ }
+ // End Property
+ } while(sie<=cend && sie>=cis);
+ // End SEMIS
+ } while(cie>=0);
+ return contType && contProp; // for use in finds, True if a type found AND all props matched
+ }
+
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java b/authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java new file mode 100644 index 00000000..ca078431 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/HttpCode.java @@ -0,0 +1,112 @@ +/*******************************************************************************
+ * ============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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.inno.env.Trans;
+
+/**
+ * HTTP Code element, which responds to the essential "handle Method".
+ *
+ * Use Native HttpServletRe[quest|sponse] calls for questions like QueryParameters (getParameter, etc)
+ *
+ * Use local "pathParam" method to obtain in an optimized manner the path parameter, which must be interpreted by originating string
+ *
+ * i.e. my/path/:id/:other/*
+ *
+ *
+ * @param <TRANS>
+ * @param <T>
+ */
+public abstract class HttpCode<TRANS extends Trans, CONTEXT> {
+ 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<String, Integer> 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<String,Integer>();
+ 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;i<pa.length && !wildcard;++i) {
+ if(pa[i].startsWith(":")) {
+ if(pa[i].endsWith("*")) {
+ val = i | pa.length<<16; // load end value in high order bits
+ key = pa[i].substring(0, pa[i].length()-1);// remove *
+ wildcard = true;
+ } else {
+ val = i;
+ key = pa[i];
+ }
+ params.put(key,val); //put in :key
+ params.put(key.substring(1,key.length()), val); // put in just key, better than adding a missing one, like Spark
+ // values[i]=null; // null stands for Variable
+ vars[i]=val;
+ } else {
+ values[i]=pa[i].getBytes();
+ if(pa[i].endsWith("*")) {
+ wildcard = true;
+ if(pa[i].length()>1) {
+ /* 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;i<start;++i) {
+ idx = path.indexOf('/',idx+1);
+ if(idx<0)break;
+ }
+ if(i==start) {
+ ++idx;
+ if(end==0) {
+ end = path.indexOf('/',idx);
+ if(end<0)end=path.length();
+ } else {
+ end=path.length();
+ }
+ return path.substring(idx,end);
+ } else if(i==start-1) { // if last spot was left blank, i.e. :key*
+ return "";
+ }
+ }
+ return null;
+ }
+
+ public boolean match(String path) {
+ if(path==null|| path.length()==0 || "/".equals(path) ) {
+ if(values==null)return true;
+ switch(values.length) {
+ case 0: return true;
+ case 1: return values[0].length==0;
+ default: return false;
+ }
+ }
+ boolean rv = true;
+ byte[] pabytes = path.getBytes();
+ int field=0;
+ int fieldIdx = 0;
+
+ int lastField = values.length;
+ int lastByte = pabytes.length;
+ boolean fieldMatched = false; // = lastByte>0?(pabytes[0]=='/'):false;
+ // IF DEBUG: System.out.println("\n -- " + path + " --");
+ for(int i=0;rv && i<lastByte;++i) {
+ if(field>=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; j<end && k<lastByte; ++k) {
+ // IF DEBUG: System.out.print((char)pabytes[k]);
+ if(pabytes[k]=='/') {
+ ++field;
+ ++j;
+ }
+ }
+
+ if(k==lastByte && pabytes[k-1]!='/')++field;
+ if(k>i)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<lastField;
+ // reset
+ fieldMatched = false;
+ fieldIdx = 0;
+ } else if(values[field].length==0) {
+ // double slash in path, but content in field. We check specially here to avoid
+ // Array out of bounds issues.
+ rv = false;
+ } else {
+ if(fieldMatched) {
+ rv =false; // field is already matched, now there's too many bytes
+ } else {
+ rv = pabytes[i]==values[field][fieldIdx++]; // compare expected (pabytes[i]) with value for particular field
+ fieldMatched=values[field].length==fieldIdx; // are all the bytes match in the field?
+ if(fieldMatched && (i==lastByte-1 || (wildcard && field==lastField-1)))
+ return true; // last field info
+ }
+ }
+ }
+ }
+ if(field!=lastField || pabytes.length!=lastByte) rv = false; // have we matched all the fields and all the bytes?
+ return rv;
+ }
+
+ public Set<String> 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 <X>
+ * @param <Y>
+ */
+public class Pair<X,Y> {
+ 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<TRANS extends Trans> implements Servlet {
+ private Routes<TRANS> routes = new Routes<TRANS>();
+
+ 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<TRANS, ?> code, String ... moreTypes) {
+ Route<TRANS> 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<TRANS> route;
+ HttpCode<TRANS,?> 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<TRANS> codesetter = new CodeSetter<TRANS>(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> 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<TRANS extends Trans> {
+ public final String auditText;
+ public final HttpMethods meth;
+ public final String path;
+
+ private Match match;
+ // package on purpose
+ private final TypedCode<TRANS> 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<TRANS>();
+ }
+
+ public void add(HttpCode<TRANS,?> code, String ... others) {
+ code.match = match;
+ content.add(code, others);
+ }
+
+// public void add(HttpCode<TRANS,?> code, Class<?> cls, String version, String ... others) {
+// code.match = match;
+// content.add(code, cls, version, others);
+// }
+//
+ public HttpCode<TRANS,?> 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<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> 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<TRANS> matches(String method, String path) {
+ return meth.name().equalsIgnoreCase(method) && match.match(path)?this:null;
+ }
+
+ public TimeTaken start(Trans trans, String auditText, HttpCode<TRANS,?> 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<TRANS, ?> 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<TRANS, ?> 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<String> contextTypes = new ArrayList<String>();
+
+}
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<TRANS extends Trans> {
+ // Since this must be very, very fast, and only needs one creation, we'll use just an array.
+ private Route<TRANS>[] 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<TRANS> findOrCreate(HttpMethods meth, String path) {
+ Route<TRANS> rv = null;
+ for(int i=0;i<end;++i) {
+ if(routes[i].resolvesTo(meth,path))rv = routes[i];
+ }
+
+ if(rv==null) {
+ if(end>=routes.length) {
+ @SuppressWarnings("unchecked")
+ Route<TRANS>[] temp = new Route[end+10];
+ System.arraycopy(routes, 0, temp, 0, routes.length);
+ routes = temp;
+ }
+
+ routes[end++]=rv=new Route<TRANS>(meth,path);
+ }
+ return rv;
+ }
+
+ public Route<TRANS> derive(HttpServletRequest req, CodeSetter<TRANS> codeSetter) throws IOException, ServletException {
+ Route<TRANS> rv = null;
+ String path = req.getPathInfo();
+ String meth = req.getMethod();
+ //TODO a TREE would be better
+ for(int i=0;rv==null && i<end; ++i) {
+ rv = routes[i].matches(meth,path);
+ if(rv!=null && !codeSetter.matches(rv)) { // potential match, check if has Code
+ rv = null; // not quite, keep going
+ }
+ }
+ //TODO a Default?
+ return rv;
+ }
+
+ public List<RouteReport> routeReport() {
+ ArrayList<RouteReport> ltr = new ArrayList<RouteReport>();
+ for(int i=0;i<end;++i) {
+ ltr.add(routes[i].api());
+ }
+ return ltr;
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java b/authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java new file mode 100644 index 00000000..244609bc --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/TransFilter.java @@ -0,0 +1,137 @@ +/*******************************************************************************
+ * ============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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.cadi.Access;
+import com.att.cadi.CadiException;
+import com.att.cadi.CadiWrap;
+import com.att.cadi.Connector;
+import com.att.cadi.Lur;
+import com.att.cadi.TrustChecker;
+import com.att.cadi.filter.CadiHTTPManip;
+import com.att.cadi.taf.TafResp;
+import com.att.cadi.taf.TafResp.RESP;
+import com.att.inno.env.Env;
+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 TransFilter<TRANS extends TransStore> 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<TRANS extends TransStore> 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 <TRANS>
+ */
+public class TypedCode<TRANS extends Trans> extends Content<TRANS> {
+ private List<Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>>> types;
+
+ public TypedCode() {
+ types = new ArrayList<Pair<String,Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>>();
+ }
+
+ /**
+ * Construct Typed Code based on ContentType parameters passed in
+ *
+ * @param code
+ * @param others
+ * @return
+ */
+ public TypedCode<TRANS> add(HttpCode<TRANS,?> 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<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> types(HttpCode<TRANS,?> code, String str) {
+ Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>> type = null;
+ ArrayList<Pair<String, Object>> props = new ArrayList<Pair<String,Object>>();
+ // Want Q percentage is to be first in the array everytime. If not listed, 1.0 is default
+ props.add(new Pair<String,Object>(Q,1f));
+ Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>> cl = new Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>>(code, props);
+// // breakup "plus" stuff, i.e. application/xaml+xml
+// int plus = str.indexOf('+');
+// if(plus<0) {
+ type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(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<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(nstr, cl);
+// types.add(type);
+// prev = plus+1;
+// plus = str.indexOf('+',prev);
+// }
+// return type;
+// }
+ }
+
+ @Override
+ protected boolean props(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> 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<String,Object>(tag,"version".equals(tag)?new Version(value):value));
+ }
+
+ public Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> prep(TRANS trans, String compare) throws IOException, ServletException {
+ Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> 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<TRANS> acc = new Acceptor<TRANS>(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<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> bestT = null;
+ for(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> 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<TRANS,?>
+// 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<TRANS, ?> code, StringBuilder sb) {
+ boolean first = true;
+ for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
+ if(code==null || pair.y.x == code) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(pair.x);
+ for(Pair<String,Object> 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<Pair<String, Object>> getContent(HttpCode<TRANS,?> code) {
+ for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> 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<HttpCode<?,?>,StringBuilder> psb = new HashMap<HttpCode<?,?>,StringBuilder>();
+ StringBuilder temp;
+ tr.desc = null;
+
+ // Read through Code/TypeCode trees for all accepted Typecodes
+ for(Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> 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<String, Object> 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<length;++i) { // match on declared parts
+ if(i==1) {
+ if(parts[1] instanceof Integer && ver.parts[1] instanceof Integer) {
+ // Match on Minor version if this Version is less than Version to be checked
+ if(((Integer)parts[1])<((Integer)ver.parts[1])) {
+ return false;
+ }
+ continue; // don't match next line
+ }
+ }
+ if(!parts[i].equals(ver.parts[i])) {
+ return false; // other spots exact match
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for(Object obj : parts) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append('.');
+ }
+ sb.append(obj.toString());
+ }
+ return sb.toString();
+ }
+}
diff --git a/authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java b/authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java new file mode 100644 index 00000000..76e589a9 --- /dev/null +++ b/authz-core/src/main/java/com/att/cssa/rserv/doc/ApiDoc.java @@ -0,0 +1,43 @@ +/*******************************************************************************
+ * ============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.doc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.att.cssa.rserv.HttpMethods;
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface ApiDoc {
+ HttpMethods method();
+ String path();
+ int expectedCode();
+ int[] errorCodes();
+ String[] text();
+ /** Format with name|type|[true|false] */
+ String[] params();
+
+}
|