Java tutorial
/* Copyright 2009-2010 Brown University and Worcester Polytechnic Institute. This file is part of Margrave. Margrave is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Margrave is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Margrave. If not, see <http://www.gnu.org/licenses/>. */ // tn package edu.wpi.margrave; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import kodkod.ast.Expression; import kodkod.ast.Formula; import org.json.*; public class SQSReader { protected static MVocab createSQSVocab(String polId) throws MGEBadIdentifierName, MGEUnknownIdentifier { MVocab env = new MVocab(); env.addSort("Principal"); env.addSort("Action"); env.addSort("Resource"); env.addSort("Condition"); // All of these sorts are pairwise disjoint // since they are incomparable in the ordering. // Decisions will be Alloy, Deny // ReqVars will be: p, a, r, c. return env; } protected static Formula handleStatementCondition(JSONObject obj, Formula theCondition, MVocab vocab) throws JSONException, MGEBadIdentifierName, MGEUnknownIdentifier, MGEManagerException { // Condition block is a conjunctive list of conditions. all must apply. // Each condition is a conjunctive list of value sets. // Each value set is a disjunctive list of values // Keys in the condition block are functions to apply // Keys in a condition are attribute names. Values are values. // Condition block // Function : {} // Function : {} // ... JSONArray conditionNames = obj.names(); for (int iCondition = 0; iCondition < conditionNames.length(); iCondition++) { String cFunction = (String) conditionNames.get(iCondition); JSONObject condition = (JSONObject) obj.get(cFunction); //MEnvironment.errorStream.println(cFunction + ": "+condition); // condition is a key:value pair or multiple such pairs. The value may be an array. // Each sub-condition must be met. HashSet<Formula> thisCondition = new HashSet<Formula>(); JSONArray subConditionNames = condition.names(); for (int iSubCondition = 0; iSubCondition < subConditionNames.length(); iSubCondition++) { String cSubKey = (String) subConditionNames.get(iSubCondition); Object subcondition = condition.get(cSubKey); // Subcondition: is it an array or a single value? if (subcondition instanceof JSONArray) { JSONArray subarr = (JSONArray) subcondition; HashSet<Formula> valuedisj = new HashSet<Formula>(); for (int iValue = 0; iValue < subarr.length(); iValue++) { //MEnvironment.errorStream.println(cFunction+"("+cSubKey+", "+subarr.get(iValue)+")"); Formula theatom = makeSQSAtom(vocab, "c", "Condition", cFunction + "<" + cSubKey + "><" + subarr.get(iValue) + ">"); valuedisj.add(theatom); } thisCondition.add(MFormulaManager.makeDisjunction(valuedisj)); } else { //MEnvironment.errorStream.println(cFunction+"("+cSubKey+", "+subcondition+")"); Formula theatom = makeSQSAtom(vocab, "c", "Condition", cFunction + "<" + cSubKey + "><" + subcondition + ">"); thisCondition.add(theatom); } } theCondition = MFormulaManager.makeAnd(theCondition, MFormulaManager.makeConjunction(thisCondition)); } return theCondition; } protected static Formula makeSQSAtom(MVocab vocab, String varname, String parentsortname, String predname) throws MGEBadIdentifierName, MGEUnknownIdentifier, MGEManagerException { // Get the variable for this varname. Expression thevar = MFormulaManager.makeVariable(varname); // Add the sort (if it doesn't already exist.) predname = MVocab.validateIdentifierFromExternalPolicy("is" + predname, true); parentsortname = MVocab.validateIdentifierFromExternalPolicy(parentsortname, true); // Kludge (for now) to make * contain subsorts: //if(predname.equalsIgnoreCase("principal.aws=*")) // parentsortname = "Principal"; //else //if(predname.equalsIgnoreCase("action=sqs:*")) // parentsortname = "action"; //else vocab.addPredicate(predname, parentsortname); if (predname.startsWith("principal.aws")) { vocab.addPredicate("principal.aws=*", "Principal"); vocab.axioms.addConstraintSubset(predname, "principal.aws=*"); } else if (predname.startsWith("action=sqs")) { vocab.addPredicate("action=sqs:*", "Action"); vocab.axioms.addConstraintSubset(predname, "action=sqs:*"); } //vocab.addSubSort(parentsortname, sortname); //MEnvironment.errorStream.println(sortname); // TODO Infer disjointness where appropriate // (e.g., Principal.AWS=555566667777 and Principal.AWS=123456789012 should be disjoint, but // Principal.AWS=555566667777 with and without dashes should not be // More ugly code: // This should be a *specific* AWS. Disjoint from all other SPECIFIC ones. if (predname.startsWith("principal.aws=") && !predname.contains("*")) { Set<String> other_candidates = vocab.getSortNamesWithPrefix("principal.aws="); // Don't disj ones with a * in them. Set<String> others = new HashSet<String>(); for (String s : other_candidates) if (!s.contains("*") && !s.equals(predname)) others.add(s); vocab.axioms.addConstraintDisjoint(predname, others); } // This should be a *specific* resource ID. Disjoint from all other specific ones if (predname.startsWith("resource=") && !predname.contains("*") && !predname.contains("&")) { Set<String> other_candidates = vocab.getSortNamesWithPrefix("resource="); // Don't disj ones with a * or & in them. Set<String> others = new HashSet<String>(); for (String s : other_candidates) if (!s.contains("*") && !s.contains("&") && !s.equals(predname)) others.add(s); vocab.axioms.addConstraintDisjoint(predname, others); } // From docs: // Although most of the information in this appendix is ***service-agnostic***, // there are some SQS-specific details you need to know. For more information, // see Special Information for SQS Policies. // So we support only the SQS-specific stuff here for now. MPredicate thepred = vocab.predicates.get(predname); return MFormulaManager.makeAtom(thevar, thepred.rel); } protected static Formula handleStatementPAR(Object obj, String varname, String parentsortname, Formula theTarget, MVocab vocab, String prepend) throws MGEUnsupportedSQS, JSONException, MGEBadIdentifierName, MGEUnknownIdentifier, MGEManagerException { // obj may be a JSONObject (Principal examples) // with a child with a value OR array of values //"Principal": { //"AWS": "*" //} //"Principal": { //"AWS": ["123456789012","555566667777"] //} // may also be a simple value (Action example) // or an array of values (Resource examples) // "Action": ["SQS:SendMessage","SQS:ReceiveMessage"], // "Resource": "/987654321098/queue1" // TODO: the example principal from "Element Descriptions" doesn't parse... //"Principal":[ // "AWS": "123456789012", // "AWS": "999999999999" // ] // is this just a bad example? // ***************************** // Step 1: Is this a JSONObject? If so, it should have only one key. Prepend that key // to all predicate names produced by its value. if (obj instanceof JSONObject) { JSONObject jobj = (JSONObject) obj; JSONArray names = jobj.names(); if (names.length() != 1) throw new MGEUnsupportedSQS("Number of keys != 1 as expected: " + obj.toString()); Object inner = jobj.get((String) names.get(0)); return handleStatementPAR(inner, varname, parentsortname, theTarget, vocab, prepend + MEnvironment.sIDBSeparator + names.get(0)); } // Now if obj is a simple value, we have a predicate name. // If it is an array of length n, we have n predicate names. if (obj instanceof JSONArray) { JSONArray jarr = (JSONArray) obj; HashSet<Formula> possibleValues = new HashSet<Formula>(); for (int ii = 0; ii < jarr.length(); ii++) { //MEnvironment.errorStream.println(prepend+"="+jarr.get(ii)); Formula theatom = makeSQSAtom(vocab, varname, parentsortname, prepend + "=" + jarr.get(ii)); possibleValues.add(theatom); } theTarget = MFormulaManager.makeAnd(theTarget, MFormulaManager.makeDisjunction(possibleValues)); } else { Formula theatom = makeSQSAtom(vocab, varname, parentsortname, prepend + "=" + obj); theTarget = MFormulaManager.makeAnd(theTarget, theatom); } return theTarget; } protected static void handleSQSStatement(JSONObject thisStatement, MPolicyLeaf result, int counter) throws JSONException, MGEUnsupportedSQS, MGEBadIdentifierName, MGEUnknownIdentifier, MGEManagerException { String ruleId = result.name + "_rule_" + counter; // Documentation says the statement ID is optional. if (!thisStatement.isNull("Sid")) ruleId = thisStatement.getString("Sid"); String effect; if (!thisStatement.isNull("Effect")) effect = thisStatement.getString("Effect"); else return; // no effect means no need to add the rule List<String> ruleNameOrdering = new ArrayList<String>(4); ruleNameOrdering.add("p"); ruleNameOrdering.add("a"); ruleNameOrdering.add("r"); ruleNameOrdering.add("c"); Formula theTarget = Formula.TRUE; Formula theCondition = Formula.TRUE; // target starts as true. All criteria must be met, so just "and" each on. // ************************* // "Principal": disjunction if (thisStatement.isNull("Principal")) return; // never applies, so don't create the rule. theTarget = handleStatementPAR(thisStatement.get("Principal"), "p", "Principal", theTarget, result.vocab, "Principal"); // ************************* // "Action": disjunction if (thisStatement.isNull("Action")) return; // never applies, so don't create the rule. theTarget = handleStatementPAR(thisStatement.get("Action"), "a", "Action", theTarget, result.vocab, "Action"); // ************************* // "Resource": disjunction if (thisStatement.isNull("Resource")) return; // never applies, so don't create the rule. theTarget = handleStatementPAR(thisStatement.get("Resource"), "r", "Resource", theTarget, result.vocab, "Resource"); // ************************* // "Condition": more complex // Condition optional? TODO -- for now if no condition, just finalize the rule. // (Why are P/A/R special?) if (!thisStatement.isNull("Condition")) theCondition = handleStatementCondition(thisStatement.getJSONObject("Condition"), theTarget, result.vocab); // Finalize the rule. result.addRule(ruleId, effect, ruleNameOrdering, theTarget, theCondition); } protected static MPolicy loadSQS(String polId, String sFileName) throws MUserException { // Convert filename sFileName = MPolicy.convertSeparators(sFileName); // Read in the JSON text BufferedReader reader; try { reader = new BufferedReader(new FileReader(sFileName)); StringBuffer target = new StringBuffer(); while (reader.ready()) { String line = reader.readLine(); target.append(line + "\n"); } reader.close(); JSONObject json = new JSONObject(target.toString()); // Use provided policy ID //String polId; //if(!json.isNull("Id")) // polId = json.getString("Id"); //else // throw new MGEUnsupportedSQS("Id element must be present."); MVocab env = createSQSVocab(polId); MPolicyLeaf result = new MPolicyLeaf(polId, env); result.declareVariable("p", "Principal"); result.declareVariable("a", "Action"); result.declareVariable("r", "Resource"); result.declareVariable("c", "Condition"); // Get statements; may be an array or a single statement object. Object statement_s = json.get("Statement"); if (statement_s instanceof JSONArray) { JSONArray statements = json.getJSONArray("Statement"); for (int ii = 0; ii < statements.length(); ii++) { JSONObject thisStatement = statements.getJSONObject(ii); handleSQSStatement(thisStatement, result, ii); } } else handleSQSStatement((JSONObject) statement_s, result, 0); // "Allow overrides a `default deny' but never an explicit deny." // Default deny is N/a. Set<String> denySet = new HashSet<String>(); denySet.add("Deny"); result.rCombineWhatOverrides.put("Allow", denySet); // Allow < {Deny} result.initIDBs(); return result; } catch (IOException e) { throw new MGEUnsupportedSQS(e.toString()); } catch (JSONException e) { throw new MGEUnsupportedSQS(e.toString()); } } }