/*
* #%L
* pro-grade
* %%
* Copyright (C) 2013 - 2014 Ondřej Lukáš, Josef Cacek
* %%
* 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.
* #L%
*/
package net.sourceforge.prograde.policyparser;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.prograde.debug.ProGradePolicyDebugger;
import net.sourceforge.prograde.type.Priority;
/**
* Class for parsing text policy file to objects which represent this policy file.
*
* @author Ondrej Lukas
*/
public class Parser {
private int lookahead;
private StreamTokenizer st;
private List<ParsedPolicyEntry> grantEntries;
private List<ParsedPolicyEntry> denyEntries;
private ParsedKeystoreEntry keystoreEntry;
private String keystorePasswordURL;
private Priority priority;
private boolean debug = false;
/**
* Constructor with predefined debug to false.
*/
public Parser() {
this(false);
}
/**
* Constructor of Parser.
*
* @param debug true for writing debug informations.
*/
public Parser(boolean debug) {
this.debug = debug;
}
/**
* Parse content of text policy file to ParsedPolicy object which represent this policy.
*
* @param file text file with policy file
* @return parsed policy file which is represented by ParsedPolicy
* @throws throw Exception when any problem occurred during parsing file (file doesn't exist, incompatible policy file etc.)
*/
public ParsedPolicy parse(File file) throws Exception {
if (file == null || !file.exists()) {
if (debug) {
if (file == null) {
ProGradePolicyDebugger.log("Given File is null");
} else {
if (!file.exists()) {
ProGradePolicyDebugger.log("Policy file " + file.getCanonicalPath() + " doesn't exists.");
}
}
}
throw new Exception("ER007: File with policy doesn't exists!");
}
if (debug) {
ProGradePolicyDebugger.log("Parsing policy " + file.getCanonicalPath());
}
final InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8");
try {
return parse(reader);
} finally {
reader.close();
}
}
/**
* Parse policy from given reader to a ParsedPolicy object.
*
* @param reader reader which provides policy content
* @return parsed policy file which is represented by ParsedPolicy
* @throws throw Exception when any problem occurred during parsing file (file doesn't exist, incompatible policy file etc.)
*/
public ParsedPolicy parse(Reader reader) throws Exception {
BufferedReader br = new BufferedReader(reader);
st = new StreamTokenizer(br);
st.resetSyntax();
st.wordChars('a', 'z');
st.wordChars('A', 'Z');
st.wordChars('.', '.');
st.wordChars('0', '9');
st.wordChars('_', '_');
st.wordChars('$', '$');
st.wordChars(128 + 32, 255);
st.whitespaceChars(0, ' ');
st.commentChar('/');
st.quoteChar('\'');
st.quoteChar('"');
st.lowerCaseMode(false);
st.ordinaryChar('/');
st.slashSlashComments(true);
st.slashStarComments(true);
grantEntries = new ArrayList<ParsedPolicyEntry>();
denyEntries = new ArrayList<ParsedPolicyEntry>();
lookahead = st.nextToken();
while (lookahead != StreamTokenizer.TT_EOF) {
switch (lookahead) {
case StreamTokenizer.TT_WORD:
String readWord = st.sval;
if (readWord.toLowerCase().equals("grant")) {
parseGrantOrDenyEntry(true);
} else {
if (readWord.toLowerCase().equals("deny")) {
parseGrantOrDenyEntry(false);
} else {
if (readWord.toLowerCase().equals("keystore")) {
parseKeystore();
} else {
if (readWord.toLowerCase().equals("keystorepasswordurl")) {
parseKeystorePassword();
} else {
if (readWord.toLowerCase().equals("priority")) {
parsePriority();
} else {
throw new Exception(
"ER008: grant, deny, keystore or keystorePasswordURL expected, but was ["
+ readWord + "]");
}
}
}
}
}
break;
case ';':
break;
default:
throw new Exception("ER009: some of keyword expected!");
}
lookahead = st.nextToken();
}
if (debug) {
for (ParsedPolicyEntry p : grantEntries) {
ProGradePolicyDebugger.log("Adding following grant entry:");
ProGradePolicyDebugger.log(p.toString());
}
for (ParsedPolicyEntry p : denyEntries) {
ProGradePolicyDebugger.log("Adding following deny entry:");
ProGradePolicyDebugger.log(p.toString());
}
if (keystoreEntry == null) {
ProGradePolicyDebugger.log("KeyStore isn't set");
} else {
ProGradePolicyDebugger.log("Adding following keystore:");
ProGradePolicyDebugger.log(keystoreEntry.toString());
}
if (keystorePasswordURL == null) {
ProGradePolicyDebugger.log("KeystorePasswordURL isn't set");
} else {
ProGradePolicyDebugger.log("Adding following keystorePasswordURL: " + keystorePasswordURL);
}
ProGradePolicyDebugger.log("Adding following priority: " + priority + "\n");
}
return new ParsedPolicy(grantEntries, denyEntries, keystoreEntry, keystorePasswordURL, priority);
}
/**
* Private method for parsing policy (grant or deny) entry.
*
* @param grantOrDeny true for grant entry, false for deny entry
* @throws throws Exception when any problem occurred during parsing policy entry
*/
private void parseGrantOrDenyEntry(boolean grantOrDeny) throws Exception {
ParsedPolicyEntry policyEntry = new ParsedPolicyEntry();
boolean nextPartExpected = true; // next part means permissions section
lookahead = st.nextToken();
while (lookahead != '{') {
switch (lookahead) {
case StreamTokenizer.TT_WORD:
String readWord = st.sval;
nextPartExpected = true;
if (readWord.toLowerCase().equals("codebase")) {
if (policyEntry.getCodebase() != null) {
throw new Exception("ER010: More codebase expression!");
}
lookahead = st.nextToken();
if (lookahead == '\"') {
policyEntry.setCodebase(st.sval);
} else {
throw new Exception("ER011: Codebase parameter have to start with \".");
}
} else {
if (readWord.toLowerCase().equals("signedby")) {
if (policyEntry.getSignedBy() != null) {
throw new Exception("ER012: More signedBy expression!");
}
lookahead = st.nextToken();
if (lookahead == '\"') {
policyEntry.setSignedBy(st.sval);
} else {
throw new Exception("ER013: SignedBy parameter have to start with \".");
}
} else {
if (readWord.toLowerCase().equals("principal")) {
policyEntry.addPrincipal(parsePrincipal());
} else {
throw new Exception("ER014: Codebase, signedBy or principal expected.");
}
}
}
break;
case ',':
if (!nextPartExpected) {
throw new Exception("ER015: Some of keywords expected, but there was [,,] instead.");
}
nextPartExpected = false;
break;
default:
throw new Exception("ER016: Some of keywords or '{' expected.");
}
lookahead = st.nextToken();
}
if (!nextPartExpected) {
throw new Exception("ER017: Some of keywords expected, but there was [,{] instead.");
}
lookahead = st.nextToken();
while (lookahead != '}') {
String readPermission = st.sval;
if (readPermission.toLowerCase().equals("permission")) {
policyEntry.addPermission(parsePermission());
}
lookahead = st.nextToken();
}
if (grantOrDeny) {
grantEntries.add(policyEntry);
} else {
denyEntries.add(policyEntry);
}
}
/**
* Private method for parsing principal part of policy entry.
*
* @return parsed principal part of policy entry
* @throws throws Exception when any problem occurred during parsing principal
*/
private ParsedPrincipal parsePrincipal() throws Exception {
lookahead = st.nextToken();
switch (lookahead) {
case '*':
lookahead = st.nextToken();
if (lookahead == '*') {
return new ParsedPrincipal(null, null);
} else {
throw new Exception("ER018: There have to be name wildcard after type wildcard.");
}
case '\"':
return new ParsedPrincipal(st.sval);
case StreamTokenizer.TT_WORD:
String principalClass = st.sval;
lookahead = st.nextToken();
switch (lookahead) {
case '*':
return new ParsedPrincipal(principalClass, null);
case '\"':
return new ParsedPrincipal(principalClass, st.sval);
default:
throw new Exception("ER019: Principal name or * expected.");
}
default:
throw new Exception("ER020: Principal type, *, or keystore alias expected.");
}
}
/**
* Private method for parsing permission part of policy entry.
*
* @return parsed permission part of policy entry
* @throws throws Exception when any problem occurred during parsing permission
*/
private ParsedPermission parsePermission() throws Exception {
ParsedPermission permission = new ParsedPermission();
lookahead = st.nextToken();
if (lookahead == StreamTokenizer.TT_WORD) {
permission.setPermissionType(st.sval);
} else {
throw new Exception("ER021: Permission type expected.");
}
lookahead = st.nextToken();
if (lookahead == '\"') {
permission.setPermissionName(st.sval);
} else {
// java.security.AllPermission possibility
if (lookahead == ';') {
return permission;
}
throw new Exception("ER022: Permission name or or [;] expected.");
}
lookahead = st.nextToken();
if (lookahead == ',') {
lookahead = st.nextToken();
boolean shouldBeSigned = false;
if (lookahead == '\"') {
String actionsWords = st.sval;
permission.setActions(actionsWords);
lookahead = st.nextToken();
switch (lookahead) {
case ',':
shouldBeSigned = true;
break;
case ';':
return permission;
default:
throw new Exception("ER023: Unexpected symbol, expected [,] or [;].");
}
lookahead = st.nextToken();
}
if (lookahead == StreamTokenizer.TT_WORD) {
String signedByWord = st.sval;
if (!signedByWord.toLowerCase().equals("signedby")) {
throw new Exception("ER024: [signedBy] expected but was [" + signedByWord + "].");
}
} else if (shouldBeSigned) {
throw new Exception("ER025: [signedBy] expected after [,].");
} else {
throw new Exception("ER026: Actions or [signedBy] expected after [,].");
}
lookahead = st.nextToken();
if (lookahead == '\"') {
permission.setSignedBy(st.sval);
} else {
throw new Exception("ER027: signedBy attribute expected.");
}
lookahead = st.nextToken();
}
if (lookahead == ';') {
return permission;
} else {
throw new Exception("ER028: [;] expected.");
}
}
/**
* Private method for parsing keystore entry.
*
* @throws throws Exception when any problem occurred during parsing keystore entry
*/
private void parseKeystore() throws Exception {
String tempKeystoreURL = null;
String tempKeystoreType = null;
String tempKeystoreProvider = null;
lookahead = st.nextToken();
if (lookahead == '\"') {
tempKeystoreURL = st.sval;
} else {
throw new Exception("ER029: [\"keystore_URL\"] expected.");
}
lookahead = st.nextToken();
if (lookahead == ',') {
lookahead = st.nextToken();
if (lookahead == '\"') {
tempKeystoreType = st.sval;
} else {
throw new Exception("ER030: [\"keystore_type\"] expected.");
}
lookahead = st.nextToken();
if (lookahead == ',') {
lookahead = st.nextToken();
if (lookahead == '\"') {
tempKeystoreProvider = st.sval;
} else {
throw new Exception("ER031: [\"keystore_provider\"] expected.");
}
lookahead = st.nextToken();
}
}
if (lookahead == ';') {
if (keystoreEntry == null) {
keystoreEntry = new ParsedKeystoreEntry(tempKeystoreURL, tempKeystoreType, tempKeystoreProvider);
}
} else {
throw new Exception("ER032: [;] expected at the end of keystore entry.");
}
}
/**
* Private method for parsing keystorePasswordURL entry.
*
* @throws throws Exception when any problem occurred during parsing keystorePasswordURL entry
*/
private void parseKeystorePassword() throws Exception {
lookahead = st.nextToken();
if (lookahead == '\"') {
if (keystorePasswordURL == null) {
keystorePasswordURL = st.sval;
}
} else {
throw new Exception("ER033: [\"keystore_password\"] expected.");
}
lookahead = st.nextToken();
if (lookahead == ';') {
return;
} else {
throw new Exception("ER034: [;] expected at the end of keystorePasswordURL entry.");
}
}
/**
* Private method for parsing priority entry.
*
* @throws throws Exception when any problem occurred during parsing priority entry
*/
private void parsePriority() throws Exception {
lookahead = st.nextToken();
if (lookahead == '\"') {
if (priority == null) {
String pr = st.sval;
if (pr.toLowerCase().equals("grant")) {
priority = Priority.GRANT;
} else {
if (pr.toLowerCase().equals("deny")) {
priority = Priority.DENY;
} else {
throw new Exception("ER035: grant or deny priority expected.");
}
}
}
} else {
throw new Exception("ER036: quotes expected after priority keyword.");
}
lookahead = st.nextToken();
if (lookahead == ';') {
return;
} else {
throw new Exception("ER037: [;] expected at the end of priority entry.");
}
}
}