Skip to content

Commit

Permalink
Merge pull request #1752 from akto-api-security/feature/modify_akto_d…
Browse files Browse the repository at this point in the history
…atatypes

allow users to modify akto data types
  • Loading branch information
Ark2307 authored Nov 22, 2024
2 parents ac2cd96 + e546534 commit ad667d0
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,32 @@ public String saveAktoDataType(){
return ERROR.toUpperCase();
}

Conditions keyConditions = null;
Conditions valueConditions = null;

try {
keyConditions = generateKeyConditions();
} catch (AktoCustomException e) {
addActionError(e.getMessage());
return ERROR.toUpperCase();
}

try {
valueConditions = generateValueConditions();
} catch (AktoCustomException e) {
addActionError(e.getMessage());
return ERROR.toUpperCase();
}

Conditions.Operator mainOperator;
try {
mainOperator = Conditions.Operator.valueOf(operator);
} catch (Exception ignored) {
addActionError("Invalid value operator");
return ERROR.toUpperCase();
}


FindOneAndUpdateOptions options = new FindOneAndUpdateOptions();
options.returnDocument(ReturnDocument.AFTER);
options.upsert(false);
Expand All @@ -275,7 +301,11 @@ public String saveAktoDataType(){
Updates.set("timestamp",Context.now()),
Updates.set("redacted",redacted),
Updates.set(AktoDataType.SAMPLE_DATA_FIXED, !redacted),
Updates.set(AktoDataType.CATEGORIES_LIST, Utils.getUniqueValuesOfList(categoriesList))
Updates.set(AktoDataType.CATEGORIES_LIST, Utils.getUniqueValuesOfList(categoriesList)),
Updates.set(AktoDataType.KEY_CONDITIONS, keyConditions),
Updates.set(AktoDataType.VALUE_CONDITIONS, valueConditions),
Updates.set(AktoDataType.OPERATOR, mainOperator),
Updates.set(AktoDataType.DATA_TYPE_PRIORITY, dataTypePriority)
),
options
);
Expand Down Expand Up @@ -759,20 +789,7 @@ public boolean forPayload(String payload, CustomDataType customDataType, Key api

}

public CustomDataType generateCustomDataType(int userId) throws AktoCustomException {
// TODO: handle errors
if (name == null || name.length() == 0) throw new AktoCustomException("Name cannot be empty");
int maxChars = 25;
if (name.length() > maxChars) throw new AktoCustomException("Maximum length allowed is "+maxChars+" characters");
name = name.trim();
name = name.toUpperCase();
if (!(name.matches("[A-Z_0-9 ]+"))) throw new AktoCustomException("Name can only contain alphabets, spaces, numbers and underscores");

if (subTypeMap.containsKey(name)) {
throw new AktoCustomException("Data type name reserved");
}


public Conditions generateKeyConditions() throws AktoCustomException {
Conditions keyConditions = null;
if (keyConditionFromUsers != null && keyOperator != null) {

Expand All @@ -798,6 +815,10 @@ public CustomDataType generateCustomDataType(int userId) throws AktoCustomExcept
}
}

return keyConditions;
}

public Conditions generateValueConditions() throws AktoCustomException {
Conditions valueConditions = null;
if (valueConditionFromUsers != null && valueOperator != null) {
Conditions.Operator vOperator;
Expand All @@ -822,6 +843,27 @@ public CustomDataType generateCustomDataType(int userId) throws AktoCustomExcept
}
}

return valueConditions;
}

public CustomDataType generateCustomDataType(int userId) throws AktoCustomException {
// TODO: handle errors
if (name == null || name.length() == 0) throw new AktoCustomException("Name cannot be empty");
int maxChars = 25;
if (name.length() > maxChars) throw new AktoCustomException("Maximum length allowed is "+maxChars+" characters");
name = name.trim();
name = name.toUpperCase();
if (!(name.matches("[A-Z_0-9 ]+"))) throw new AktoCustomException("Name can only contain alphabets, spaces, numbers and underscores");

if (subTypeMap.containsKey(name)) {
throw new AktoCustomException("Data type name reserved");
}


Conditions keyConditions = generateKeyConditions();
Conditions valueConditions = generateValueConditions();


if ((keyConditions == null || keyConditions.getPredicates() == null || keyConditions.getPredicates().size() == 0) &&
(valueConditions == null || valueConditions.getPredicates() ==null || valueConditions.getPredicates().size() == 0)) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,20 @@ function DataTypes() {

const saveAction = async () => {
if (currState.dataType === 'Akto') {

const keyArr = currState.keyConditions.predicates.map(transform.convertMapFunction)
const valueArr = currState.valueConditions.predicates.map(transform.convertMapFunction)

let obj = {
name: currState.name,
redacted:currState.redacted,
categoriesList: currState?.categoriesList || [],
operator: currState.operator,
keyConditionFromUsers: keyArr,
keyOperator: currState.keyConditions.operator,
valueConditionFromUsers: valueArr,
valueOperator: currState.valueConditions.operator,
dataTypePriority: currState?.priority ? currState.priority.toUpperCase() : "",
...transform.convertToSensitiveData(currState.sensitiveState),

}
Expand Down Expand Up @@ -246,7 +256,6 @@ function DataTypes() {
initial={currState.active} label="Active" />
: null}
<Dropdown
disabled={currState.dataType !== 'Custom'}
menuItems={severityItems}
initial={currState.priority}
selected={(val) => {handleChange({priority: val})}}
Expand Down Expand Up @@ -408,7 +417,7 @@ function DataTypes() {
</VerticalStack>
)

let components = (!isNew && currState.dataType === 'Akto') ? [descriptionCard, requestCard, redactCard] : [descriptionCard, conditionsCard, requestCard, redactCard, TestTemplateCard]
let components = (!isNew && currState.dataType === 'Akto') ? [descriptionCard, conditionsCard, requestCard, redactCard] : [descriptionCard, conditionsCard, requestCard, redactCard, TestTemplateCard]

return (
<DetailsPage
Expand Down
44 changes: 44 additions & 0 deletions libs/dao/src/main/java/com/akto/dto/AktoDataType.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import com.akto.dto.data_types.Conditions;
import com.akto.dto.type.SingleTypeInfo;
import com.akto.util.enums.GlobalEnums.Severity;

Expand All @@ -24,6 +25,13 @@ public class AktoDataType {
public static final String DATA_TYPE_PRIORITY = "dataTypePriority";
private Severity dataTypePriority;

public static final String KEY_CONDITIONS = "keyConditions";
Conditions keyConditions;
public static final String VALUE_CONDITIONS = "valueConditions";
Conditions valueConditions;
public static final String OPERATOR = "operator";
Conditions.Operator operator;

public AktoDataType() {
}
public AktoDataType(String name, boolean sensitiveAlways, List<SingleTypeInfo.Position> sensitivePosition,int timestamp, IgnoreData ignoreData, boolean redacted, boolean sampleDataFixed) {
Expand Down Expand Up @@ -108,4 +116,40 @@ public List<String> getCategoriesList() {
public void setCategoriesList(List<String> categoriesList) {
this.categoriesList = categoriesList;
}

public Conditions getKeyConditions() {
return keyConditions;
}

public void setKeyConditions(Conditions keyConditions) {
this.keyConditions = keyConditions;
}

public Conditions getValueConditions() {
return valueConditions;
}

public void setValueConditions(Conditions valueConditions) {
this.valueConditions = valueConditions;
}

public Conditions.Operator getOperator() {
return operator;
}

public void setOperator(Conditions.Operator operator) {
this.operator = operator;
}

public boolean validate(Object value, Object key) {
try {
return this.validateRaw(value, key);
} catch (Exception e) {
return false;
}
}

public boolean validateRaw(Object value, Object key) throws Exception {
return CustomDataType.validateRawUtility(value, key, this.keyConditions, this.valueConditions, this.operator);
}
}
21 changes: 13 additions & 8 deletions libs/dao/src/main/java/com/akto/dto/CustomDataType.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,26 @@ public boolean validate(Object value, Object key) {
}

public boolean validateRaw(Object value, Object key) throws Exception {
if (this.keyConditions == null && this.valueConditions==null) return false;
return validateRawUtility(value, key, this.keyConditions, this.valueConditions, this.operator);
}


public static boolean validateRawUtility(Object value, Object key, Conditions keyConditions, Conditions valueConditions, Conditions.Operator operator) {
if (keyConditions == null && valueConditions==null) return false;
boolean keyResult = true;
if (this.keyConditions != null) {
keyResult = this.keyConditions.validate(key);
if (keyConditions != null) {
keyResult = keyConditions.validate(key);
}

boolean valueResult = true;
if (this.valueConditions != null) {
valueResult = this.valueConditions.validate(value);
if (valueConditions != null) {
valueResult = valueConditions.validate(value);
}

if (this.valueConditions ==null || this.keyConditions == null) {
if (valueConditions ==null || keyConditions == null) {
return keyResult && valueResult;
} else {
switch (this.operator) {
switch (operator) {
case AND:
return keyResult && valueResult;
case OR:
Expand All @@ -103,7 +108,7 @@ public boolean validateRaw(Object value, Object key) throws Exception {
}
}
}

public ObjectId getId() {
return id;
}
Expand Down
63 changes: 57 additions & 6 deletions libs/dao/src/main/java/com/akto/dto/type/KeyTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.regex.Pattern;

import com.akto.dao.context.Context;
import com.akto.dto.AktoDataType;
import com.akto.dto.CustomDataType;
import com.akto.dto.IgnoreData;
import com.akto.dto.SensitiveParamInfo;
Expand Down Expand Up @@ -110,6 +111,47 @@ private static boolean checkForSubtypesTest(ParamId paramId, IgnoreData ignoreDa
return true;
}

private static boolean aktoDataTypeChanged(String name) {
Map<String, AktoDataType> aktoDataTypeMap = SingleTypeInfo.getAktoDataTypeMap(Context.accountId.get());
AktoDataType aktoDataType = aktoDataTypeMap.get(name);
return aktoDataType != null && (aktoDataType.getKeyConditions() != null || aktoDataType.getValueConditions() != null);
}

private static boolean matchesAktoDataType(String name, Object key, Object value) {
Map<String, AktoDataType> aktoDataTypeMap = SingleTypeInfo.getAktoDataTypeMap(Context.accountId.get());
AktoDataType aktoDataType = aktoDataTypeMap.get(name);
return aktoDataType != null && aktoDataType.validate(value, key);
}

private static SubType matchesSubType(SingleTypeInfo.SubType subType, Object key, Object val) {
String name = subType.getName();
// check if user has overriden the default behaviour of the subtype
if (aktoDataTypeChanged(name)) {
if (matchesAktoDataType(name, key, val)) {
return subType;
}
} else {
switch (name) {
case "CREDIT_CARD":
if (isCreditCard(val.toString())) return subType;
break;
case "JWT":
if (isJWT(val.toString())) return subType;
break;
case "IP_ADDRESS":
if (isIP(val.toString())) return subType;
break;
case "PHONE_NUMBER":
if (isPhoneNumber(val.toString())) return subType;
break;
default:
return null;
}
}

return null;
}

private static SubType getSubtype(Object o,String key, boolean checkForSubtypes){
if (o == null) {
return SingleTypeInfo.NULL;
Expand All @@ -129,7 +171,7 @@ private static SubType getSubtype(Object o,String key, boolean checkForSubtypes)
}

String oString = o.toString();
if (checkForSubtypes && isCreditCard(oString)) {
if (checkForSubtypes && matchesSubType(SingleTypeInfo.CREDIT_CARD, key, oString) != null) {
return SingleTypeInfo.CREDIT_CARD;
}

Expand Down Expand Up @@ -165,19 +207,28 @@ private static SubType getSubtype(Object o,String key, boolean checkForSubtypes)
for(Map.Entry<SubType, Pattern> entry: patternToSubType.entrySet()) {
Pattern pattern = entry.getValue();
SubType subType = entry.getKey();
if( ( checkForSubtypes || subType.getName().equals("URL") ) && pattern.matcher(oString).matches()) {
return subType;
String name = subType.getName();

if (aktoDataTypeChanged(name)) {
if (matchesAktoDataType(name, key, oString)) {
return subType;
}
} else {
if( ( checkForSubtypes || subType.getName().equals("URL") ) && pattern.matcher(oString).matches()) {
return subType;
}
}
}
if (checkForSubtypes && isJWT(oString)) {

if (checkForSubtypes && matchesSubType(SingleTypeInfo.JWT, key, oString) != null) {
return SingleTypeInfo.JWT;
}

if (checkForSubtypes && isPhoneNumber(oString)) {
if (checkForSubtypes && matchesSubType(SingleTypeInfo.PHONE_NUMBER, key, oString) != null) {
return SingleTypeInfo.PHONE_NUMBER;
}

if (checkForSubtypes && isIP(oString)) {
if (checkForSubtypes && matchesSubType(SingleTypeInfo.IP_ADDRESS, key, oString) != null) {
return SingleTypeInfo.IP_ADDRESS;
}

Expand Down
Loading

0 comments on commit ad667d0

Please sign in to comment.