Skip to content

Commit

Permalink
shadowRef improvements annotation/intention improvements. Added prote…
Browse files Browse the repository at this point in the history
…cted string annotation/intention if secrets providers aren't used.
  • Loading branch information
1azyman committed Mar 13, 2024
1 parent 3042e5c commit 04ee5c0
Show file tree
Hide file tree
Showing 11 changed files with 455 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlTagValue;
import com.intellij.xml.util.XmlTagUtil;
import org.jetbrains.annotations.NotNull;

/**
* Created by Viliam Repan (lazyman).
*/
public class DeprecatedElementAnnotator implements Annotator {
public class DeprecatedElementAnnotator implements Annotator, MidPointAnnotator {

@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
Expand Down Expand Up @@ -49,9 +50,21 @@ public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder hold
return;
}

holder.newAnnotation(HighlightSeverity.WARNING, "Element marked as deprecated (since " + deprecatedSince + ")")
String msg = "Element marked as deprecated (since " + deprecatedSince + ")";
String tooltip = "Element marked as deprecated (since <b>" + deprecatedSince + "</b>)";

if (xmlElement instanceof XmlTag tag) {
createNewAnnotation(XmlTagUtil.getStartTagNameElement(tag), holder, msg, tooltip);
createNewAnnotation(XmlTagUtil.getEndTagNameElement(tag), holder, msg, tooltip);
} else {
createNewAnnotation(xmlElement, holder, msg, tooltip);
}
}

private void createNewAnnotation(XmlElement element, AnnotationHolder holder, String msg, String tooltip) {
holder.newAnnotation(HighlightSeverity.WARNING, msg)
.range(element.getTextRange())
.tooltip("Element marked as deprecated (since <b>" + deprecatedSince + "</b>)")
.tooltip(tooltip)
.highlightType(ProblemHighlightType.LIKE_DEPRECATED)
.create();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.evolveum.midpoint.studio.impl.lang.annotation;

import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.lang.annotation.AnnotationBuilder;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlToken;
import com.intellij.xml.util.XmlTagUtil;

public interface MidPointAnnotator {

default void createTagAnnotations(XmlTag tag, AnnotationHolder holder, HighlightSeverity severity, String msg, IntentionAction fix) {
createTagAnnotations(tag, holder, severity, msg, null, fix);
}

default void createTagAnnotations(XmlTag tag, AnnotationHolder holder, HighlightSeverity severity, String msg, String tooltip, IntentionAction fix) {
createNewAnnotation(XmlTagUtil.getStartTagNameElement(tag), holder, severity, msg, tooltip, fix);
createNewAnnotation(XmlTagUtil.getEndTagNameElement(tag), holder, severity, msg, tooltip, fix);
}

default void createNewAnnotation(XmlToken token, AnnotationHolder holder, HighlightSeverity severity, String msg, String tooltip, IntentionAction fix) {
if (token == null) {
return;
}

if (tooltip == null) {
tooltip = msg;
}

AnnotationBuilder builder = holder.newAnnotation(severity, msg)
.range(token)
.tooltip(tooltip);

if (fix != null) {
builder = builder.withFix(fix);
}

builder.create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.evolveum.midpoint.studio.impl.lang.annotation;

import com.evolveum.midpoint.schema.SchemaConstantsGenerated;
import com.evolveum.midpoint.studio.util.MidPointUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowAssociationType;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;

public class ShadowRefAnnotation implements Annotator, MidPointAnnotator {

private static final String MESSAGE =
"shadowRef element could represent possible problem when moving this midPoint " +
"object to another environment. Reason is reference to specific oid which would " +
"be different in another environment.";

@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
Project project = element.getProject();
if (!MidPointUtils.hasMidPointFacet(project)) {
return;
}

XmlTag shadowRef = getShadowRefTag(element);
if (shadowRef == null) {
return;
}

if (!hasValueExpressionAsParentTag(shadowRef)) {
return;
}

createTagAnnotations(shadowRef, holder, HighlightSeverity.WARNING, MESSAGE, null);
}

private XmlTag getShadowRefTag(PsiElement element) {
if (!(element instanceof XmlTag tag)) {
return null;
}

if (!Objects.equals(ShadowAssociationType.F_SHADOW_REF, MidPointUtils.createQName(tag))) {
return null;
}

return tag;
}

private boolean hasValueExpressionAsParentTag(XmlTag tag) {
if (tag == null) {
return false;
}

XmlTag parent = tag.getParentTag();

return Objects.equals(SchemaConstantsGenerated.C_VALUE, MidPointUtils.createQName(parent));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.evolveum.midpoint.studio.impl.lang.intention;

import com.evolveum.midpoint.studio.impl.lang.annotation.MidPointAnnotator;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.codeInspection.util.IntentionFamilyName;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.psi.xml.XmlTag;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import javax.xml.namespace.QName;

public abstract class MidPointAnnotatorIntention extends PsiElementBaseIntentionAction implements Annotator, MidPointAnnotator {

public MidPointAnnotatorIntention(String name) {
setText(name);
}

@Override
public @NotNull @IntentionFamilyName String getFamilyName() {
return getText();
}

@Override
public boolean startInWriteAction() {
return false;
}

protected XmlTag createTag(XmlTag parent, QName name) {
return createTag(parent, name, null);
}

protected XmlTag createTag(XmlTag parent, QName name, String body) {
String prefix = parent.getPrefixByNamespace(name.getNamespaceURI());

String tagPrefix = StringUtils.isBlank(prefix) ? "" : prefix + ":";
String namespace = prefix == null ? name.getNamespaceURI() : null;

XmlTag child = parent.createChildTag(tagPrefix + name.getLocalPart(), namespace, body, false);

return parent.addSubTag(child, false);
}

protected void createTagAnnotations(XmlTag tag, AnnotationHolder holder, String msg) {
createTagAnnotations(tag, holder, HighlightSeverity.WARNING, msg, this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package com.evolveum.midpoint.studio.impl.lang.intention;

import com.evolveum.midpoint.schema.SchemaConstantsGenerated;
import com.evolveum.midpoint.studio.util.MidPointUtils;
import com.evolveum.midpoint.studio.util.PsiUtils;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlToken;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ThrowableRunnable;
import org.jetbrains.annotations.NotNull;

import javax.xml.namespace.QName;
import java.util.Objects;

public class ProtectedStringAnnotatorIntention extends MidPointAnnotatorIntention {

private static final String NAME = "Use secrets provider";

private static final QName PROVIDER = new QName(SchemaConstantsGenerated.NS_TYPES, "provider");
private static final QName KEY = new QName(SchemaConstantsGenerated.NS_TYPES, "key");

public ProtectedStringAnnotatorIntention() {
super(NAME);
}

@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
XmlTag value = getProtectedStringTag(element.getParent());
if (value == null) {
return;
}

ThrowableRunnable<RuntimeException> task = () -> {
XmlTag externalData = createTag(value, ProtectedStringType.F_EXTERNAL_DATA);
createTag(externalData, PROVIDER, "INSERT_PROVIDER_NAME");
createTag(externalData, KEY, "INSERT_KEY_NAME");

deleteSubTag(value, ProtectedStringType.F_CLEAR_VALUE);
deleteSubTag(value, ProtectedStringType.F_HASHED_DATA);
deleteSubTag(value, ProtectedStringType.F_ENCRYPTED_DATA);
};

WriteCommandAction.writeCommandAction(project)
.withName(NAME)
.withGroupId(NAME)
.run(task);
}

private void deleteSubTag(XmlTag parent, QName subTagName) {
XmlTag subTag = MidPointUtils.findSubTag(parent, subTagName);
if (subTag != null) {
subTag.delete();
}
}

@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
if (!MidPointUtils.hasMidPointFacet(project)) {
return false;
}

// are we in element that is one of the problematic
// elements (clearValue, encryptedData, hashedData) in protected string?

if (!(element instanceof XmlToken)) {
return false;
}

if (!(element.getParent() instanceof XmlTag tag)) {
return false;
}

return getClearValueTag(tag) != null
|| getEncryptedDataTag(tag) != null
|| getHashedDataTag(tag) != null;
}

@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
Project project = element.getProject();
if (!MidPointUtils.hasMidPointFacet(project)) {
return;
}

XmlTag clearValue = getClearValueTag(element);
if (clearValue != null) {
String msg = "Clear value element represents possible security problem, the reason is that it contains " +
"clear value which is not safe. Secret providers configuration should be used.";

createTagAnnotations(clearValue, holder, msg);
}

XmlTag encryptedData = getEncryptedDataTag(element);
if (encryptedData != null) {
String msg = "Encrypted data element represents possible problem when moving this midPoint " +
"object to another environment. Secret providers configuration should be used.";

createTagAnnotations(encryptedData, holder, msg);
}

XmlTag hashedData = getHashedDataTag(element);
if (hashedData != null) {
String msg = "Hashed data element represents possible problem when moving this midPoint " +
"object to another environment. Secret providers configuration should be used.";

createTagAnnotations(hashedData, holder, msg);
}
}

private XmlTag getClearValueTag(PsiElement value) {
return getProtectedStringItem(value, ProtectedStringType.F_CLEAR_VALUE);
}

private XmlTag getEncryptedDataTag(PsiElement value) {
return getProtectedStringItem(value, ProtectedStringType.F_ENCRYPTED_DATA);
}

private XmlTag getHashedDataTag(PsiElement value) {
return getProtectedStringItem(value, ProtectedStringType.F_HASHED_DATA);
}

/**
* @return XmlTag if {@link PsiElement} element is tag and a child inside c:ProtectedStringType with
* specific name, null otherwise
*/
private XmlTag getProtectedStringItem(PsiElement element, QName name) {
if (!(element instanceof XmlTag tag)) {
return null;
}

if (!Objects.equals(name, MidPointUtils.createQName(tag))) {
return null;
}

XmlTag ps = getProtectedStringTag(tag);
if (ps == null) {
return null;
}

return tag;
}

/**
* @param element should be a direct child tag of c:ProtectedStringType otherwise returns null
*/
private XmlTag getProtectedStringTag(PsiElement element) {
if (!(element instanceof XmlTag tag)) {
return null;
}

XmlTag protectedString = tag.getParentTag();
if (protectedString == null) {
return null;
}

QName type = PsiUtils.getTagXsdType(protectedString);
if (!Objects.equals(ProtectedStringType.COMPLEX_TYPE, type)) {
return null;
}

return protectedString;
}
}

0 comments on commit 04ee5c0

Please sign in to comment.