Make it possible to validate a code.
This commit is contained in:
57
src/main.zig
57
src/main.zig
@@ -59,6 +59,13 @@ fn mainInternal() !void {
|
||||
const authenticator = try getAuthenticator(allocator, arg.show.?, arg.config_location);
|
||||
const code = try authenticator.code(allocator, std.time.timestamp);
|
||||
try std.io.getStdOut().writer().print("{s}\n", .{code});
|
||||
} else if (arg.validateAuthenticator != null and arg.validateCode != null) {
|
||||
const authenticator = try getAuthenticator(allocator, arg.validateAuthenticator.?, arg.config_location);
|
||||
const matches = try authenticator.validate(allocator, arg.validateCode.?, std.time.timestamp);
|
||||
if (!matches) {
|
||||
std.process.exit(1);
|
||||
}
|
||||
std.process.exit(0);
|
||||
} else {
|
||||
printHelp();
|
||||
}
|
||||
@@ -71,6 +78,7 @@ fn printHelp() void {
|
||||
\\Commands:
|
||||
\\ list List the configured authentiators
|
||||
\\ show NAME Show the code for the authenticator with name NAME
|
||||
\\ validate NAME CODE Validate the code CODE for the authenticator with name NAME
|
||||
\\
|
||||
\\Options:
|
||||
\\ --bash Print script to set up Bash shell integration. Usage: add
|
||||
@@ -82,11 +90,7 @@ fn printHelp() void {
|
||||
std.debug.print("{s}", .{msg});
|
||||
}
|
||||
|
||||
const Args = struct {
|
||||
list: bool,
|
||||
show: ?[]const u8,
|
||||
config_location: []const u8,
|
||||
};
|
||||
const Args = struct { list: bool, show: ?[]const u8, config_location: []const u8, validateAuthenticator: ?[]const u8, validateCode: ?[]const u8 };
|
||||
|
||||
const OtpAuthUrl = struct { name: []const u8, secretEncoded: []const u8, url: []const u8, period: u32, digits: u4, algorithm: []const u8 };
|
||||
|
||||
@@ -148,6 +152,12 @@ const Authenticator = struct {
|
||||
//debug("code as 0-padded string: {s} ({d})\n", .{ result, token });
|
||||
return result;
|
||||
}
|
||||
|
||||
fn validate(self: Authenticator, allocator: Allocator, expectedCode: []const u8, timeFunc: *const fn () i64) !bool {
|
||||
const actualCode = try self.code(allocator, timeFunc);
|
||||
defer allocator.free(actualCode);
|
||||
return eql(u8, actualCode, expectedCode);
|
||||
}
|
||||
};
|
||||
|
||||
fn zeroPad(allocator: Allocator, digits: u4, x: anytype) ![]u8 {
|
||||
@@ -176,11 +186,7 @@ fn zeroPad(allocator: Allocator, digits: u4, x: anytype) ![]u8 {
|
||||
const ArgumentError = error{ InvalidOtpAuthUrl, UnknownParameter, MissingConfigLocation, AuthenticatorNotFound, MissingAuthenticatorParam, FailedToOpenConfigFile, TooManyParsinErrors };
|
||||
|
||||
fn parseArgs(allocator: Allocator, args: []const []const u8) !Args {
|
||||
var result = Args{
|
||||
.list = false,
|
||||
.show = null,
|
||||
.config_location = try configLocation(allocator),
|
||||
};
|
||||
var result = Args{ .list = false, .show = null, .config_location = try configLocation(allocator), .validateAuthenticator = null, .validateCode = null };
|
||||
var i: u17 = 1;
|
||||
while (i < args.len) : (i += 1) {
|
||||
const arg = args[i];
|
||||
@@ -189,14 +195,17 @@ fn parseArgs(allocator: Allocator, args: []const []const u8) !Args {
|
||||
const msg =
|
||||
\\_zig_totp_completions()
|
||||
\\{
|
||||
\\ if [ "${COMP_WORDS[$COMP_CWORD-1]}" = 'show' ]
|
||||
\\ if [ "${COMP_WORDS[$COMP_CWORD-1]}" = 'show' ] || [ "${COMP_WORDS[$COMP_CWORD-1]}" = 'validate' ]
|
||||
\\ then
|
||||
\\ COMPREPLY=($(compgen -W "$(zig-totp list 2> /dev/null )" -- "${COMP_WORDS[$COMP_CWORD]}" ))
|
||||
\\ elif [ "${COMP_WORDS[$COMP_CWORD-2]}" = 'validate' ]
|
||||
\\ then
|
||||
\\ COMPREPLY=($(compgen -W "CODE" -- "${COMP_WORDS[$COMP_CWORD]}"))
|
||||
\\ elif [ "${COMP_WORDS[$COMP_CWORD-1]}" = '--config' ]
|
||||
\\ then
|
||||
\\ COMPREPLY=($(compgen -A file -- "${COMP_WORDS[$COMP_CWORD]}" ))
|
||||
\\ else
|
||||
\\ COMPREPLY=($(compgen -W "list show --bash --config" -- "${COMP_WORDS[$COMP_CWORD]}" ))
|
||||
\\ COMPREPLY=($(compgen -W "list show validate --bash --config" -- "${COMP_WORDS[$COMP_CWORD]}" ))
|
||||
\\ fi
|
||||
\\}
|
||||
\\complete -F _zig_totp_completions zig-totp
|
||||
@@ -220,6 +229,14 @@ fn parseArgs(allocator: Allocator, args: []const []const u8) !Args {
|
||||
return error.MissingAuthenticatorParam;
|
||||
}
|
||||
result.show = args[i];
|
||||
} else if (eql(u8, "validate", arg)) {
|
||||
i += 2;
|
||||
if (i >= args.len) {
|
||||
std.debug.print("expected two parameters for 'validate'. The name for an authenticator and a code to validate.\n", .{});
|
||||
return error.MissingValidateParam;
|
||||
}
|
||||
result.validateAuthenticator = args[i - 1];
|
||||
result.validateCode = args[i];
|
||||
} else {
|
||||
std.debug.print("unknown argument: {s}\n", .{arg});
|
||||
return ArgumentError.UnknownParameter;
|
||||
@@ -577,3 +594,19 @@ test "secret is not Base32 - invalid character" {
|
||||
const actualError = authenticator.code(std.testing.allocator, _closure_return_1725695340);
|
||||
try std.testing.expectError(Base32Error.InvalidCharacter, actualError);
|
||||
}
|
||||
|
||||
test "testcases for 'validate'" {
|
||||
const secretForSha1 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; // plain: "12345678901234567890" hex: "3132333435363738393031323334353637383930"
|
||||
|
||||
try testValidate(OtpAuthUrl{ .name = "", .secretEncoded = secretForSha1, .url = "", .period = 30, .digits = 8, .algorithm = "SHA1" }, _closure_return_59, "94287082", true);
|
||||
|
||||
try testValidate(OtpAuthUrl{ .name = "", .secretEncoded = secretForSha1, .url = "", .period = 30, .digits = 8, .algorithm = "SHA1" }, _closure_return_1111111109, "07081804", true);
|
||||
|
||||
try testValidate(OtpAuthUrl{ .name = "", .secretEncoded = secretForSha1, .url = "", .period = 30, .digits = 8, .algorithm = "SHA1" }, _closure_return_1111111109, "12345678", false);
|
||||
}
|
||||
fn testValidate(otpAuthUrl: OtpAuthUrl, timeFunc: *const fn () i64, expected: []const u8, expectedMatch: bool) !void {
|
||||
const authenticator = Authenticator{ .url = otpAuthUrl };
|
||||
|
||||
const match = try authenticator.validate(std.testing.allocator, expected, timeFunc);
|
||||
try std.testing.expectEqual(expectedMatch, match);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user