Skip to content

Commit

Permalink
Various improvements to the API
Browse files Browse the repository at this point in the history
  • Loading branch information
ancailliau committed Sep 9, 2022
1 parent d7f167c commit 88d40f7
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 26 deletions.
55 changes: 53 additions & 2 deletions Synsharp.Tests/TestNodeHelper.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Channels;
using System.Threading.Tasks;
using NUnit.Framework;
using Synsharp.Forms;
using Synsharp.Types;
using CryptoX509Cert = Synsharp.Forms.CryptoX509Cert;
using InetIPv6 = Synsharp.Forms.InetIPv6;
using InetUrl = Synsharp.Forms.InetUrl;

namespace Synsharp.Tests;

Expand All @@ -30,12 +33,12 @@ public async Task TestAddNodeWithTags()
Assert.NotNull(SynapseClient);

var inetIPv6 = InetIPv6.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
inetIPv6.Tags = new HashSet<string>() { "di.test", "random.tag" };
inetIPv6.Tags.Add("di.test", "random.tag");

var response = await SynapseClient.Nodes.Add(inetIPv6);

Assert.IsNotNull(response);
Assert.AreEqual(4, response.Tags.Count);
Assert.AreEqual(4, response.Tags.Count());
Assert.That(response.Tags.Contains("di"));
Assert.That(response.Tags.Contains("di.test"));
Assert.That(response.Tags.Contains("random"));
Expand All @@ -55,4 +58,52 @@ public async Task TestAddCryptoX509CertNode()
Assert.IsNotNull(response);
Assert.AreEqual("ebff56c59290e26d64050e0b68ec6575", response.MD5.ToString());
}


[Test]
public async Task TestAddTag()
{
Assert.NotNull(SynapseClient);

var inetIPv6 = InetIPv6.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
Console.WriteLine($"'{string.Join(",", inetIPv6.Tags)}'");
inetIPv6 = await SynapseClient.Nodes.Add(inetIPv6);

await SynapseClient.Nodes.AddTag(inetIPv6.Iden, "di.test");
var response = await SynapseClient.Nodes.GetAsync<InetIPv6>(inetIPv6.Iden);
Console.WriteLine($"'{string.Join(",", response.Tags)}'");
Assert.That(response.Tags.Contains("di.test"));
}

[Test]
public async Task TestDelete()
{
Assert.NotNull(SynapseClient);

var inetIPv6 = InetIPv6.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
inetIPv6 = await SynapseClient.Nodes.Add(inetIPv6);

var response = SynapseClient.StormAsync<InetIPv6>("inet:ipv6=2001:0db8:85a3:0000:0000:8a2e:0370:7334");
Assert.AreEqual(1, await response.CountAsync());

await SynapseClient.Nodes.Remove(inetIPv6.Iden);

response = SynapseClient.StormAsync<InetIPv6>("inet:ipv6=2001:0db8:85a3:0000:0000:8a2e:0370:7334");
Assert.AreEqual(0, await response.CountAsync());
}

[Test]
public async Task TestGetByProperty()
{
Assert.NotNull(SynapseClient);

var url = InetUrl.Parse("https://www.welivesecurity.com/2020/03/12/tracking-turla-new-backdoor-armenian-watering-holes/");
_ = await SynapseClient.Nodes.Add(url);

var response = SynapseClient.Nodes.GetAsyncByProperty<InetUrl>(new Dictionary<string, string>()
{
{ "fqdn", "www.welivesecurity.com" }
});
Assert.AreEqual(1, await response.CountAsync());
}
}
6 changes: 6 additions & 0 deletions Synsharp/Forms/HashSHA1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ namespace Synsharp.Forms;
[SynapseForm("hash:sha1")]
public class HashSHA1 : SynapseObject<Hex>
{
public static HashSHA1 Parse(string str)
{
var hash = new HashSHA1();
hash.SetValue(Hex.Parse(str));
return hash;
}
}
6 changes: 6 additions & 0 deletions Synsharp/Forms/HashSHA256.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ namespace Synsharp.Forms;
[SynapseForm("hash:sha256")]
public class HashSHA256 : SynapseObject<Hex>
{
public static HashSHA256 Parse(string str)
{
var hash = new HashSHA256();
hash.SetValue(Hex.Parse(str));
return hash;
}
}
8 changes: 8 additions & 0 deletions Synsharp/Forms/InetFqdn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ namespace Synsharp.Forms;
[SynapseForm("inet:fqdn")]
public class InetFqdn : SynapseObject<Str>
{
public InetFqdn() : base()
{
}

public InetFqdn(string s)
{
SetValue(s);
}
}
5 changes: 5 additions & 0 deletions Synsharp/Forms/InetIPv4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public InetIPv4() : base()
{
}

public InetIPv4(IPAddress s)
{
SetValue(s);
}

public InetIPv4(string s)
{
SetValue(s);
Expand Down
26 changes: 26 additions & 0 deletions Synsharp/Forms/InetUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,32 @@
* limitations under the License.
*/

using System;
using Synsharp.Attribute;

namespace Synsharp.Forms;

[SynapseForm("inet:url")]
public class InetUrl : SynapseObject<Types.InetUrl>
{
protected bool Equals(InetUrl other)
{
return base.Equals(other) && Equals(Value, other.Value);
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((InetUrl)obj);
}

public override int GetHashCode()
{
return Value.GetHashCode();
}

public InetUrl() : base()
{
}
Expand All @@ -39,4 +58,11 @@ public InetUrl(string str) : base()
[SynapseProperty("port")] public Types.InetPort Port { get; set; }
[SynapseProperty("proto")] public Types.Str Proto { get; set; }
[SynapseProperty("user")] public Types.InetUser User { get; set; }

public static InetUrl Parse(string str)
{
var address = new InetUrl();
address.SetValue(str);
return address;
}
}
26 changes: 26 additions & 0 deletions Synsharp/Forms/SynForm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2022 Antoine Cailliau
*
* 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.
*/

using Synsharp.Attribute;
using Synsharp.Types;

namespace Synsharp.Forms;

[SynapseForm("syn:form")]
public class SynForm : SynapseObject<Str>
{

}
112 changes: 90 additions & 22 deletions Synsharp/Helpers/NodeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,35 @@ public NodeHelper(SynapseClient synapseClient, ILogger<NodeHelper> logger)
public async Task<T> Add<T>(T synapseObject, string view = null) where T : SynapseObject
{
var value = GetCoreValueDynamic(synapseObject);
var propertyDict = GetPropertyDict(synapseObject);

var tagRegex = new Regex(@"[a-zA-Z0-9\.]+");
_logger.LogDebug($"Got {synapseObject.Tags.Count()}");
if (synapseObject.Tags.Any(_ => !tagRegex.Match(_).Success))
throw new SynapseException("Tags should match [a-zA-Z0-9.]+");

// Build the storm command and execute
var attribute = synapseObject.GetType().GetCustomAttribute<SynapseFormAttribute>();
if (attribute != null)
{
var type = attribute.Name;
var attributes = string.Join(" ", propertyDict.Select(_ => $":{_.Key}={_.Value}"));
var tags = string.Join(" ", synapseObject.Tags.Select(_ => $"+#{_}"));

var command = $"[ {type}=\"{StringHelpers.Escape(value)}\" {attributes} {tags} ]";
var results = await _synapseClient.StormAsync<T>(command,new ApiStormQueryOpts(){View= view}).ToListAsync();
return results.FirstOrDefault();
}
else
{
throw new SynapseException($"Could not infer form type for '{synapseObject.GetType().FullName}'");
}
}

private Dictionary<string, string> GetPropertyDict<T>(T synapseObject) where T : SynapseObject
{
var propertyDict = new Dictionary<string, string>();

// Get the form properties
var fields = SynapseConverter.GetFields(synapseObject.GetType());
foreach (var field in fields)
Expand All @@ -62,7 +88,7 @@ public async Task<T> Add<T>(T synapseObject, string view = null) where T : Synap
}
}
}

var properties = SynapseConverter.GetProperties(synapseObject.GetType());
foreach (var propertyInfo in properties)
{
Expand All @@ -79,31 +105,14 @@ public async Task<T> Add<T>(T synapseObject, string view = null) where T : Synap
}
else
{
throw new SynapseException($"The property '{propertyInfo.Name}' has no value or value is not a SynapseType.");
throw new SynapseException(
$"The property '{propertyInfo.Name}' has no value or value is not a SynapseType.");
}
}
}
}

var tagRegex = new Regex(@"[a-zA-Z0-9\.]+");
if (synapseObject.Tags.Any(_ => !tagRegex.Match(_).Success))
throw new SynapseException("Tags should match [a-zA-Z0-9.]+");

// Build the storm command and execute
var attribute = synapseObject.GetType().GetCustomAttribute<SynapseFormAttribute>();
if (attribute != null)
{
var type = attribute.Name;
var attributes = string.Join(" ", propertyDict.Select(_ => $":{_.Key}={_.Value}"));
var tags = string.Join(" ", synapseObject.Tags.Select(_ => $"+#{_}"));
var command = $"[ {type}={value} {attributes} {tags} ]";
var results = await _synapseClient.StormAsync<T>(command,new ApiStormQueryOpts(){View= view}).ToListAsync();
return results.FirstOrDefault();
}
else
{
throw new SynapseException($"Could not infer form type for '{synapseObject.GetType().FullName}'");
}
return propertyDict;
}

private static string GetCoreValueDynamic<T>(T synapseObject) where T : SynapseObject
Expand Down Expand Up @@ -164,6 +173,15 @@ public async Task AddLightEdge(SynapseObject o1, SynapseObject o2, string @ref,
_ = await _synapseClient.StormAsync<object>(command, new ApiStormQueryOpts(){View= view}).ToListAsync();
}

public async Task RemoveLightEdge(SynapseObject o1, SynapseObject o2, string @ref, string view = null)
{
var (t1, v1) = GetSelector(o1);
var (t2, v2) = GetSelector(o2);

var command = $"{t1}={v1} [ <({@ref})- {{ {t2}={v2} }} ]";
_ = await _synapseClient.StormAsync<object>(command, new ApiStormQueryOpts(){View= view}).ToListAsync();
}

/// <summary>
/// Returns the node identified by the specified iden
/// </summary>
Expand All @@ -184,4 +202,54 @@ public async Task<T> GetAsync<T>(string iden, string view = null)
})
.SingleOrDefaultAsync();
}

public async Task Remove(string iden, string viewIden = null)
{
if (iden == null) throw new ArgumentNullException(nameof(iden));
_ = (await _synapseClient.StormAsync<SynapseObject>(
$"| delnode",
new ApiStormQueryOpts() { View = viewIden, Idens = new[] { iden } })
.ToListAsync());
}

public async Task AddTag(string iden, string tagName, string viewIden = null)
{
if (iden == null) throw new ArgumentNullException(nameof(iden));
var tagRegex = new Regex(@"[a-zA-Z0-9\.]+");
if (!tagRegex.Match(tagName).Success)
throw new SynapseException("Tags should match [a-zA-Z0-9.]+");
_ = (await _synapseClient.StormAsync<SynapseObject>(
$"[ +#{tagName} ]",
new ApiStormQueryOpts() { View = viewIden, Idens = new[] { iden } })
.ToListAsync());
}

public IAsyncEnumerable<T> GetAsyncByProperty<T>(Dictionary<string,string> propertyValues, string view = null)
{
var attribute = typeof(T).GetCustomAttribute<SynapseFormAttribute>();
if (attribute != null)
{
var type = attribute.Name;

string selector = "{type}";
if (propertyValues.Count == 1)
{
var p = propertyValues.Single();
selector = $"{type}:{p.Key}={p.Value}";
}
else if (propertyValues.Count > 1)
{
var plist = propertyValues.ToList();
var p = plist.First();
var attributes = string.Join(" ", plist.Skip(1).Select(_ => $"+:{_.Key}={_.Value}"));
selector = $"{type}:{p.Key}={p.Value} {attributes}";
}

return _synapseClient.StormAsync<T>(selector,new ApiStormQueryOpts(){View= view});
}
else
{
throw new SynapseException($"Could not infer form type for '{typeof(T).FullName}'");
}
}
}
2 changes: 1 addition & 1 deletion Synsharp/Helpers/ViewHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public IAsyncEnumerable<T> Execute<T>(string iden, string query)
return _client.StormAsync<T>($"view.exec {iden} {{ {query} }}");
}

public object Merge(string iden)
public Task Merge(string iden)
{
var command = $"$view = $lib.view.get({iden}) $view.merge()";
return _client.StormCallAsync(command);
Expand Down
3 changes: 2 additions & 1 deletion Synsharp/SynapseObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using System.Net;
using System.Reflection;
using System.Text;
using Newtonsoft.Json;
using Synsharp.Attribute;
using Synsharp.Forms;
using Synsharp.Types;
Expand All @@ -34,7 +35,7 @@ public abstract class SynapseObject
[SynapseProperty(".created")] public Time Created { set; get; }
[SynapseProperty("iden")] public Str Iden { set; get; }

public HashSet<string> Tags { get; set; } = new();
public TagTree Tags { get; set; } = new();

public abstract string GetCoreValue();
}
Expand Down
Loading

0 comments on commit 88d40f7

Please sign in to comment.