Skip to content

Commit

Permalink
Merge pull request #193 from datalust/dev
Browse files Browse the repository at this point in the history
2021.2 Maintenance Release
  • Loading branch information
nblumhardt authored May 7, 2021
2 parents 8f4fb03 + 0828c8c commit ea581b4
Show file tree
Hide file tree
Showing 25 changed files with 763 additions and 30 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ Install or unzip the [release for your operating system](https://github.com/data
dotnet tool install --global seqcli
```

To set a default server URL, run:
To set a default server URL and API key, run:

```
seqcli config -k connection.serverUrl -v https://your-seq-server
seqcli config -k connection.apiKey -v your-api-key
```

The API key will be stored in your `SeqCli.json` configuration file; on Windows, this is encrypted using DPAPI; on Mac/Linux the key is currently stored in plain text. As an alternative to storing the API key in configuration, it can be passed to each command via the `--apikey=` argument.
Expand Down
1 change: 1 addition & 0 deletions seqcli.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=hackily/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mdash/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=retentionpolicies/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=retentionpolicy/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=roastery/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=seqcli/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean>
Expand Down
88 changes: 83 additions & 5 deletions src/SeqCli/Cli/Commands/ApiKey/CreateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Seq.Api;
using Seq.Api.Model.Inputs;
using Seq.Api.Model.LogEvents;
using Seq.Api.Model.Security;
using Seq.Api.Model.Shared;
using SeqCli.Cli.Features;
using SeqCli.Config;
using SeqCli.Connection;
using SeqCli.Levels;
using SeqCli.Util;
using Serilog;

namespace SeqCli.Cli.Commands.ApiKey
{
Expand All @@ -36,8 +39,9 @@ class CreateCommand : Command
readonly PropertiesFeature _properties;
readonly OutputFormatFeature _output;

string _title, _token, _filter, _level;
bool _useServerTimestamps;
string _title, _token, _filter, _level, _connectUsername, _connectPassword;
string[] _permissions;
bool _useServerTimestamps, _connectPasswordStdin;

public CreateCommand(SeqConnectionFactory connectionFactory, SeqCliConfig config)
{
Expand Down Expand Up @@ -70,15 +74,36 @@ public CreateCommand(SeqConnectionFactory connectionFactory, SeqCliConfig config
"Discard client-supplied timestamps and use server clock values",
_ => _useServerTimestamps = true);

Options.Add(
"permissions=",
"The permissions to delegate to the API key; the default is `Ingest`",
v => _permissions = ArgumentString.NormalizeList(v));

Options.Add(
"connect-username=",
"A username to connect with, useful primarily when setting up the first API key",
v => _connectUsername = ArgumentString.Normalize(v));

Options.Add(
"connect-password=",
"When `connect-username` is specified, a corresponding password",
v => _connectPassword = ArgumentString.Normalize(v));

Options.Add(
"connect-password-stdin",
"When `connect-username` is specified, read the corresponding password from `STDIN`",
_ => _connectPasswordStdin = true);

_connection = Enable<ConnectionFeature>();
_output = Enable(new OutputFormatFeature(config.Output));
}

protected override async Task<int> Run()
{
var connection = _connectionFactory.Connect(_connection);

// Default will apply the ingest permission
var connection = await TryConnectAsync();
if (connection == null)
return 1;

var apiKey = await connection.ApiKeys.TemplateAsync();

apiKey.Title = _title;
Expand All @@ -104,6 +129,25 @@ protected override async Task<int> Run()
apiKey.InputSettings.MinimumLevel = Enum.Parse<LogEventLevel>(LevelMapping.ToFullLevelName(_level));
}

apiKey.AssignedPermissions.Clear();
if (_permissions != null)
{
foreach (var permission in _permissions)
{
if (!Enum.TryParse<Permission>(permission, out var p))
{
Log.Error("Unrecognized permission {Permission}", p);
return 1;
}

apiKey.AssignedPermissions.Add(p);
}
}
else
{
apiKey.AssignedPermissions.Add(Permission.Ingest);
}

apiKey = await connection.ApiKeys.AddAsync(apiKey);

if (_token == null && !_output.Json)
Expand All @@ -117,5 +161,39 @@ protected override async Task<int> Run()

return 0;
}

async Task<SeqConnection> TryConnectAsync()
{
SeqConnection connection;
if (_connectUsername != null)
{
if (_connection.IsApiKeySpecified)
{
Log.Error("The `connect-username` and `apikey` options are mutually exclusive");
return null;
}

if (_connectPasswordStdin)
{
if (_connectPassword != null)
{
Log.Error("The `connect-password` and `connect-password-stdin` options are mutually exclusive");
return null;
}

_connectPassword = await Console.In.ReadLineAsync();
}

var (url, _) = _connectionFactory.GetConnectionDetails(_connection);
connection = new SeqConnection(url);
await connection.Users.LoginAsync(_connectUsername, _connectPassword ?? "");
}
else
{
connection = _connectionFactory.Connect(_connection);
}

return connection;
}
}
}
102 changes: 102 additions & 0 deletions src/SeqCli/Cli/Commands/Feed/CreateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2018 Datalust Pty Ltd and Contributors
//
// 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 System;
using System.Threading.Tasks;
using SeqCli.Cli.Features;
using SeqCli.Config;
using SeqCli.Connection;
using SeqCli.Util;
using Serilog;

namespace SeqCli.Cli.Commands.Feed
{
[Command("feed", "create", "Create a NuGet feed",
Example = "seqcli feed create -n 'CI' --location=\"https://f.feedz.io/example/ci\" -u Seq --password-stdin")]
class CreateCommand : Command
{
readonly SeqConnectionFactory _connectionFactory;

readonly ConnectionFeature _connection;
readonly OutputFormatFeature _output;

string _name, _location, _username, _password;
bool _passwordStdin;

public CreateCommand(SeqConnectionFactory connectionFactory, SeqCliConfig config)
{
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));

Options.Add(
"n=|name=",
"A unique name for the feed",
n => _name = ArgumentString.Normalize(n));

Options.Add(
"l=|location=",
"The feed location; this may be a NuGet v2 or v3 feed URL, or a local filesystem path on the Seq server",
l => _location = ArgumentString.Normalize(l));

Options.Add(
"u=|username=",
"The username Seq should supply when connecting to the feed, if authentication is required",
n => _username = ArgumentString.Normalize(n));

Options.Add(
"p=|password=",
"A feed password, if authentication is required; note that `--password-stdin` is more secure",
p => _password = ArgumentString.Normalize(p));

Options.Add(
"password-stdin",
"Read the feed password from `STDIN`",
_ => _passwordStdin = true);

_connection = Enable<ConnectionFeature>();
_output = Enable(new OutputFormatFeature(config.Output));
}

protected override async Task<int> Run()
{
var connection = _connectionFactory.Connect(_connection);

var feed = await connection.Feeds.TemplateAsync();
feed.Name = _name;
feed.Location = _location;
feed.Username = _username;

if (_passwordStdin)
{
if (_password != null)
{
Log.Error("The `password` and `password-stdin` options are mutually exclusive");
return 1;
}

_password = await Console.In.ReadLineAsync();
}

if (_password != null)
{
feed.NewPassword = _password;
}

feed = await connection.Feeds.AddAsync(feed);

_output.WriteEntity(feed);

return 0;
}
}
}
71 changes: 71 additions & 0 deletions src/SeqCli/Cli/Commands/Feed/ListCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2018 Datalust Pty Ltd and Contributors
//
// 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 System;
using System.Linq;
using System.Threading.Tasks;
using SeqCli.Cli.Features;
using SeqCli.Config;
using SeqCli.Connection;

namespace SeqCli.Cli.Commands.Feed
{
[Command("feed", "list", "List NuGet feeds", Example="seqcli feed list")]
class ListCommand : Command
{
readonly SeqConnectionFactory _connectionFactory;

readonly ConnectionFeature _connection;
readonly OutputFormatFeature _output;

string _name, _id;

public ListCommand(SeqConnectionFactory connectionFactory, SeqCliConfig config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));

Options.Add(
"n=|name=",
"The name of the feed to list",
n => _name = n);

Options.Add(
"i=|id=",
"The id of a single feed to list",
id => _id = id);

_output = Enable(new OutputFormatFeature(config.Output));
_connection = Enable<ConnectionFeature>();
}

protected override async Task<int> Run()
{
var connection = _connectionFactory.Connect(_connection);

var list = _id != null ?
new[] { await connection.Feeds.FindAsync(_id) } :
(await connection.Feeds.ListAsync())
.Where(f => _name == null || _name == f.Name)
.ToArray();

foreach (var feed in list)
{
_output.WriteEntity(feed);
}

return 0;
}
}
}
Loading

0 comments on commit ea581b4

Please sign in to comment.