working commit

This commit is contained in:
2026-06-05 18:25:03 +02:00
commit 7d8abba003
82 changed files with 21863 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
MAX_SIZE=1024 # 1 MB limit in KB
for file in $(git diff --cached --name-only --diff-filter=ACM); do
if [ ! -e "$file" ]; then continue; fi
size=$(du -k "$file" | cut -f1)
if [ $size -gt $MAX_SIZE ]; then
echo "Error: File $file is larger than the allowed size of $((MAX_SIZE / 1024)) MB."
exit 1
fi
done
status=0
for file in $(git diff --cached --name-only | grep -E '\.go$'); do
badfile=$(gofmt -l "$file")
if test -n "$badfile" ; then
echo "Error: file needs gofmt: $badfile"
status=1
fi
done
# If any files were not formatted, exit with a non-zero status to abort the commit.
if [ "$status" -ne 0 ]; then
echo "git pre-commit check failed: some Go files are not formatted."
exit 1
fi
exit 0
+24
View File
@@ -0,0 +1,24 @@
*~
autom4te.cache
Makefile
config.status
config.log
cmd/mbased/mbased
cmd/mbasectl/mbasectl
*.db
*.db-shm
*.db-wal
*.tar.*
*.tmp.*
*.tar
*.bin
tmp
mbased
mbasectl
DIST
*.tar.gz
*.deb
*.rpm
mbased.service
variant.go
initrc/mbased
View File
+22
View File
@@ -0,0 +1,22 @@
FROM alpine:3.20 as builder
RUN apk --no-cache add make binutils gcc libc-dev automake autoconf curl
RUN curl -o /usr/local/lib/go.tar.gz https://dl.google.com/go/go1.24.4.linux-amd64.tar.gz
RUN cd /usr/local/lib && tar xzf go.tar.gz
RUN cd /usr/local/bin && ln -sf ../lib/go/bin/* .
WORKDIR /app/src/
COPY go.mod go.sum .
COPY . .
RUN ./configure --prefix=/app
RUN make clean all install
RUN rm -rf /app/src
FROM alpine:3.20
COPY --from=builder /app /app
RUN chmod 1777 /var
WORKDIR /app
#USER daemon:daemon
ENTRYPOINT ["/app/sbin/certmanagerd"]
+119
View File
@@ -0,0 +1,119 @@
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
SUFFIXES = .go
OBJEXT= none
sbin_PROGRAMS = mbased mbasedump mbaserestore mbaseadmin
bin_PROGRAMS = mbasectl
mbased_SOURCES = cmd/mbased/main.go
mbased$(EXEEXT): $(mbased_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbased$(EXEEXT) $(mbased_SOURCES)
mbasedump_SOURCES = cmd/mbasedump/maindump.go
mbasedump$(EXEEXT): $(mbasedump_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbasedump$(EXEEXT) $(mbasedump_SOURCES)
mbaserestore_SOURCES = cmd/mbaserestore/mainrestore.go
mbaserestore$(EXEEXT): $(mbaserestore_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbaserestore$(EXEEXT) $(mbaserestore_SOURCES)
mbasectl_SOURCES = cmd/mbasectl/main.go \
cmd/mbasectl/account.go \
cmd/mbasectl/grant.go \
cmd/mbasectl/dump.go
mbasectl$(EXEEXT): $(mbasectl_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o mbasectl$(EXEEXT) $(mbasectl_SOURCES)
mbaseadmin_SOURCES = cmd/mbaseadmin/main.go \
cmd/mbaseadmin/account.go \
cmd/mbaseadmin/grant.go
mbaseadmin$(EXEEXT): $(mbaseadmin_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbaseadmin$(EXEEXT) $(mbaseadmin_SOURCES)
EXTRA_mbased_SOURCES =
EXTRA_DIST = \
Changelog.txt \
Containerfile \
etc/mbase/mbased.yaml \
.gitignore \
go.mod \
go.sum \
proto/cmctl.proto \
README.md \
test/account_test.go \
test/dump_test.go \
test/hello_test.go \
test/Makefile.am \
test/Makefile.in \
test/server.go \
test/support.go \
vendor/*
GENDIR=pkg/mbctl
PROTOSRC = proto/mbctl.proto
rpc:
mkdir -p $(GENDIR)
$(PROTOC) --proto_path=proto --go_out=$(GENDIR) --go-grpc_out=$(GENDIR) $(PROTOSRC)
SYSTEMD_LIBDIR = /lib/systemd/system
FREEBSD_LOCALBASE = /usr/local
FREEBSD_RCDIR = $(FREEBSD_LOCALBASE)/etc/rc.d
LINUX_SYSTEMDDIR = /lib/systemd/system
install-data-local:
test -z $(DESTDIR)$(SRV_CONFDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_CONFDIR)
test -z $(DESTDIR)$(SRV_LOGDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_LOGDIR)
test -z $(DESTDIR)$(SRV_RUNDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_RUNDIR)
test -z $(DESTDIR)$(SRV_DATADIR) || $(MKDIR_P) $(DESTDIR)$(SRV_DATADIR)
test -z $(DESTDIR)$(SYSTEMD_LIBDIR) || $(MKDIR_P) $(DESTDIR)$(SYSTEMD_LIBDIR)
if FREEBSD_OS
test -z $(DESTDIR)$(FREEBSD_RCDIR) || $(MKDIR_P) $(DESTDIR)$(FREEBSD_RCDIR)
$(INSTALL_DATA) initrc/mbased $(DESTDIR)$(FREEBSD_RCDIR)
chmod a+x $(DESTDIR)$(FREEBSD_RCDIR)/mbased
endif
if LINUX_OS
if SYSTEMD
test -z $(DESTDIR)$(LINUX_SYSTEMDDIR) || $(MKDIR_P) $(DESTDIR)$(LINUX_SYSTEMDDIR)
$(INSTALL_DATA) initrc/mbased.service $(DESTDIR)$(LINUX_SYSTEMDDIR)
endif
endif
format:
for dir in $$(find app pkg cmd -type d); do \
(cd $$dir && $(GO) fmt .); \
done
run:
test -z $(DESTDIR)$(SRV_LOGDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_LOGDIR)
test -z $(DESTDIR)$(SRV_RUNDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_RUNDIR)
test -z $(DESTDIR)$(SRV_DATADIR) || $(MKDIR_P) $(DESTDIR)$(SRV_DATADIR)
env CGO_ENABLED=1 $(GO) run $(GOFLAGS) ./cmd/mbased/... --daemon=false
distclean-local: clean
rm -rf autom4te.cache
clean-local:
rm -f */*/*~
rm -f */*~
rm -f *~
rm -f cmd/mbaseadmin/mbaseadmin
rm -f cmd/mbasectl/mbasectl
rm -f cmd/mbased/mbased
rm -f cmd/mbasedump/mbasedump
rm -f cmd/mbaselocal/mbaselocal
rm -f cmd/mbaserestore/mbaserestore
rm -rf autom4te.cache
rm -rf tmp/
+925
View File
@@ -0,0 +1,925 @@
# Makefile.in generated by automake 1.17 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2024 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
am__rm_f = rm -f $(am__rm_f_notfound)
am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
sbin_PROGRAMS = mbased$(EXEEXT) mbasedump$(EXEEXT) \
mbaserestore$(EXEEXT) mbaseadmin$(EXEEXT)
bin_PROGRAMS = mbasectl$(EXEEXT)
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
$(am__configure_deps) $(am__DIST_COMMON)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_CLEAN_FILES = app/config/variant.go initrc/mbased.service \
initrc/mbased
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)"
PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS)
am_mbaseadmin_OBJECTS =
mbaseadmin_OBJECTS = $(am_mbaseadmin_OBJECTS)
mbaseadmin_LDADD = $(LDADD)
am_mbasectl_OBJECTS =
mbasectl_OBJECTS = $(am_mbasectl_OBJECTS)
mbasectl_LDADD = $(LDADD)
am_mbased_OBJECTS =
mbased_OBJECTS = $(am_mbased_OBJECTS)
mbased_LDADD = $(LDADD)
am_mbasedump_OBJECTS =
mbasedump_OBJECTS = $(am_mbasedump_OBJECTS)
mbasedump_LDADD = $(LDADD)
am_mbaserestore_OBJECTS =
mbaserestore_OBJECTS = $(am_mbaserestore_OBJECTS)
mbaserestore_LDADD = $(LDADD)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@
depcomp =
am__maybe_remake_depfiles =
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(mbaseadmin_SOURCES) $(mbasectl_SOURCES) $(mbased_SOURCES) \
$(EXTRA_mbased_SOURCES) $(mbasedump_SOURCES) \
$(mbaserestore_SOURCES)
DIST_SOURCES = $(mbaseadmin_SOURCES) $(mbasectl_SOURCES) \
$(mbased_SOURCES) $(EXTRA_mbased_SOURCES) $(mbasedump_SOURCES) \
$(mbaserestore_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
AM_RECURSIVE_TARGETS = cscope
am__DIST_COMMON = $(srcdir)/Makefile.in \
$(top_srcdir)/app/config/variant.go.in \
$(top_srcdir)/initrc/mbased.in \
$(top_srcdir)/initrc/mbased.service.in README.md compile \
config.guess config.sub install-sh missing
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
am__remove_distdir = \
if test -d "$(distdir)"; then \
find "$(distdir)" -type d ! -perm -700 -exec chmod u+rwx {} ';' \
; rm -rf "$(distdir)" \
|| { sleep 5 && rm -rf "$(distdir)"; }; \
else :; fi
am__post_remove_distdir = $(am__remove_distdir)
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = -9
DIST_TARGETS = dist-gzip
# Exists only to be overridden by the user if desired.
AM_DISTCHECK_DVI_TARGET = dvi
distuninstallcheck_listfiles = find . -type f -print
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
distcleancheck_listfiles = \
find . \( -type f -a \! \
\( -name .nfs* -o -name .smb* -o -name .__afs* \) \) -print
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CP = @CP@
CPIO = @CPIO@
CPPFLAGS = @CPPFLAGS@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
CYGPATH_W = @CYGPATH_W@
DBUILDPACKAGE = @DBUILDPACKAGE@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
ETAGS = @ETAGS@
EXEEXT = @EXEEXT@
GO = @GO@
HAVE_GO = @HAVE_GO@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
OBJEXT = none
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PODMAN = @PODMAN@
PROTOC = @PROTOC@
ROOT_GROUP = @ROOT_GROUP@
RPMBUILD = @RPMBUILD@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
SRV_CONFDIR = @SRV_CONFDIR@
SRV_DATADIR = @SRV_DATADIR@
SRV_LOGDIR = @SRV_LOGDIR@
SRV_RUNDIR = @SRV_RUNDIR@
STRIP = @STRIP@
VERSION = @VERSION@
XARGS = @XARGS@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
srv_confdir = @srv_confdir@
srv_datadir = @srv_datadir@
srv_devel_mode = @srv_devel_mode@
srv_logdir = @srv_logdir@
srv_name = @srv_name@
srv_rundir = @srv_rundir@
srv_sbindir = @srv_sbindir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
SUFFIXES = .go
mbased_SOURCES = cmd/mbased/main.go
mbasedump_SOURCES = cmd/mbasedump/maindump.go
mbaserestore_SOURCES = cmd/mbaserestore/mainrestore.go
mbasectl_SOURCES = cmd/mbasectl/main.go \
cmd/mbasectl/account.go \
cmd/mbasectl/grant.go \
cmd/mbasectl/dump.go
mbaseadmin_SOURCES = cmd/mbaseadmin/main.go \
cmd/mbaseadmin/account.go \
cmd/mbaseadmin/grant.go
EXTRA_mbased_SOURCES =
EXTRA_DIST = \
Changelog.txt \
Containerfile \
etc/mbase/mbased.yaml \
.gitignore \
go.mod \
go.sum \
proto/cmctl.proto \
README.md \
test/account_test.go \
test/dump_test.go \
test/hello_test.go \
test/Makefile.am \
test/Makefile.in \
test/server.go \
test/support.go \
vendor/*
GENDIR = pkg/mbctl
PROTOSRC = proto/mbctl.proto
SYSTEMD_LIBDIR = /lib/systemd/system
FREEBSD_LOCALBASE = /usr/local
FREEBSD_RCDIR = $(FREEBSD_LOCALBASE)/etc/rc.d
LINUX_SYSTEMDDIR = /lib/systemd/system
all: all-am
.SUFFIXES:
.SUFFIXES: .go
am--refresh: Makefile
@:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
$(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
&& exit 0; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
echo ' $(SHELL) ./config.status'; \
$(SHELL) ./config.status;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(SHELL) ./config.status --recheck
$(top_srcdir)/configure: $(am__configure_deps)
$(am__cd) $(srcdir) && $(AUTOCONF)
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
$(am__aclocal_m4_deps):
app/config/variant.go: $(top_builddir)/config.status $(top_srcdir)/app/config/variant.go.in
cd $(top_builddir) && $(SHELL) ./config.status $@
initrc/mbased.service: $(top_builddir)/config.status $(top_srcdir)/initrc/mbased.service.in
cd $(top_builddir) && $(SHELL) ./config.status $@
initrc/mbased: $(top_builddir)/config.status $(top_srcdir)/initrc/mbased.in
cd $(top_builddir) && $(SHELL) ./config.status $@
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p \
; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n;h' \
-e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) files[d] = files[d] " " $$1; \
else { print "f", $$3 "/" $$4, $$1; } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
$(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
} \
; done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-e 's/$$/$(EXEEXT)/' \
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && $(am__rm_f) $$files
clean-binPROGRAMS:
-$(am__rm_f) $(bin_PROGRAMS)
install-sbinPROGRAMS: $(sbin_PROGRAMS)
@$(NORMAL_INSTALL)
@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p \
; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n;h' \
-e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) files[d] = files[d] " " $$1; \
else { print "f", $$3 "/" $$4, $$1; } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
$(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
} \
; done
uninstall-sbinPROGRAMS:
@$(NORMAL_UNINSTALL)
@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-e 's/$$/$(EXEEXT)/' \
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(sbindir)" && $(am__rm_f) $$files
clean-sbinPROGRAMS:
-$(am__rm_f) $(sbin_PROGRAMS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscope: cscope.files
test ! -s cscope.files \
|| $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
clean-cscope:
-rm -f cscope.files
cscope.files: clean-cscope cscopelist
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
$(am__remove_distdir)
$(AM_V_at)$(MKDIR_P) "$(distdir)"
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
-test -n "$(am__skip_mode_fix)" \
|| find "$(distdir)" -type d ! -perm -755 \
-exec chmod u+rwx,go+rx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r "$(distdir)"
dist-gzip: distdir
tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
$(am__post_remove_distdir)
dist-bzip2: distdir
tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
$(am__post_remove_distdir)
dist-lzip: distdir
tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
$(am__post_remove_distdir)
dist-xz: distdir
tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
$(am__post_remove_distdir)
dist-zstd: distdir
tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
$(am__post_remove_distdir)
dist-tarZ: distdir
@echo WARNING: "Support for distribution archives compressed with" \
"legacy program 'compress' is deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
$(am__post_remove_distdir)
dist-shar: distdir
@echo WARNING: "Support for shar distribution archives is" \
"deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
$(am__post_remove_distdir)
dist-zip: distdir
-rm -f $(distdir).zip
zip -rq $(distdir).zip $(distdir)
$(am__post_remove_distdir)
dist dist-all:
$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
$(am__post_remove_distdir)
# This target untars the dist file and tries a VPATH configuration. Then
# it guarantees that the distribution is self-contained by making another
# tarfile.
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
eval GZIP= gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
*.tar.lz*) \
lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
*.tar.xz*) \
xz -dc $(distdir).tar.xz | $(am__untar) ;;\
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
eval GZIP= gzip -dc $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
*.tar.zst*) \
zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
esac
chmod -R a-w $(distdir)
chmod u+w $(distdir)
mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
chmod a-w $(distdir)
test -d $(distdir)/_build || exit 0; \
dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
&& dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
&& am__cwd=`pwd` \
&& $(am__cd) $(distdir)/_build/sub \
&& ../../configure \
$(AM_DISTCHECK_CONFIGURE_FLAGS) \
$(DISTCHECK_CONFIGURE_FLAGS) \
--srcdir=../.. --prefix="$$dc_install_base" \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
&& $(MAKE) $(AM_MAKEFLAGS) check \
&& $(MAKE) $(AM_MAKEFLAGS) install \
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
&& $(MAKE) $(AM_MAKEFLAGS) uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
distuninstallcheck \
&& chmod -R a-w "$$dc_install_base" \
&& ({ \
(cd ../.. && umask 077 && mkdir "$$dc_destdir") \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
} || { rm -rf "$$dc_destdir"; exit 1; }) \
&& rm -rf "$$dc_destdir" \
&& $(MAKE) $(AM_MAKEFLAGS) dist \
&& rm -rf $(DIST_ARCHIVES) \
&& $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
&& cd "$$am__cwd" \
|| exit 1
$(am__post_remove_distdir)
@(echo "$(distdir) archives ready for distribution: "; \
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
distuninstallcheck:
@test -n '$(distuninstallcheck_dir)' || { \
echo 'ERROR: trying to run $@ with an empty' \
'$$(distuninstallcheck_dir)' >&2; \
exit 1; \
}; \
$(am__cd) '$(distuninstallcheck_dir)' || { \
echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
exit 1; \
}; \
test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left after uninstall:" ; \
if test -n "$(DESTDIR)"; then \
echo " (check DESTDIR support)"; \
fi ; \
$(distuninstallcheck_listfiles) ; \
exit 1; } >&2
distcleancheck: distclean
@if test '$(srcdir)' = . ; then \
echo "ERROR: distcleancheck can only run from a VPATH build" ; \
exit 1 ; \
fi
@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left in build directory after distclean:" ; \
$(distcleancheck_listfiles) ; \
exit 1; } >&2
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
installdirs:
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-binPROGRAMS clean-generic clean-local \
clean-sbinPROGRAMS mostlyclean-am
distclean: distclean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-local distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am: install-data-local
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-binPROGRAMS install-sbinPROGRAMS
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf $(top_srcdir)/autom4te.cache
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS uninstall-sbinPROGRAMS
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-am clean \
clean-binPROGRAMS clean-cscope clean-generic clean-local \
clean-sbinPROGRAMS cscope cscopelist-am ctags ctags-am dist \
dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
dist-xz dist-zip dist-zstd distcheck distclean \
distclean-compile distclean-generic distclean-local \
distclean-tags distcleancheck distdir distuninstallcheck dvi \
dvi-am html html-am info info-am install install-am \
install-binPROGRAMS install-data install-data-am \
install-data-local install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-sbinPROGRAMS install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-binPROGRAMS uninstall-sbinPROGRAMS
.PRECIOUS: Makefile
mbased$(EXEEXT): $(mbased_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbased$(EXEEXT) $(mbased_SOURCES)
mbasedump$(EXEEXT): $(mbasedump_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbasedump$(EXEEXT) $(mbasedump_SOURCES)
mbaserestore$(EXEEXT): $(mbaserestore_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbaserestore$(EXEEXT) $(mbaserestore_SOURCES)
mbasectl$(EXEEXT): $(mbasectl_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o mbasectl$(EXEEXT) $(mbasectl_SOURCES)
mbaseadmin$(EXEEXT): $(mbaseadmin_SOURCES) $(EXTRA_mbased_SOURCES)
env CGO_ENABLED=1 $(GO) build $(GOFLAGS) -o mbaseadmin$(EXEEXT) $(mbaseadmin_SOURCES)
rpc:
mkdir -p $(GENDIR)
$(PROTOC) --proto_path=proto --go_out=$(GENDIR) --go-grpc_out=$(GENDIR) $(PROTOSRC)
install-data-local:
test -z $(DESTDIR)$(SRV_CONFDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_CONFDIR)
test -z $(DESTDIR)$(SRV_LOGDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_LOGDIR)
test -z $(DESTDIR)$(SRV_RUNDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_RUNDIR)
test -z $(DESTDIR)$(SRV_DATADIR) || $(MKDIR_P) $(DESTDIR)$(SRV_DATADIR)
test -z $(DESTDIR)$(SYSTEMD_LIBDIR) || $(MKDIR_P) $(DESTDIR)$(SYSTEMD_LIBDIR)
@FREEBSD_OS_TRUE@ test -z $(DESTDIR)$(FREEBSD_RCDIR) || $(MKDIR_P) $(DESTDIR)$(FREEBSD_RCDIR)
@FREEBSD_OS_TRUE@ $(INSTALL_DATA) initrc/mbased $(DESTDIR)$(FREEBSD_RCDIR)
@FREEBSD_OS_TRUE@ chmod a+x $(DESTDIR)$(FREEBSD_RCDIR)/mbased
@LINUX_OS_TRUE@@SYSTEMD_TRUE@ test -z $(DESTDIR)$(LINUX_SYSTEMDDIR) || $(MKDIR_P) $(DESTDIR)$(LINUX_SYSTEMDDIR)
@LINUX_OS_TRUE@@SYSTEMD_TRUE@ $(INSTALL_DATA) initrc/mbased.service $(DESTDIR)$(LINUX_SYSTEMDDIR)
format:
for dir in $$(find app pkg cmd -type d); do \
(cd $$dir && $(GO) fmt .); \
done
run:
test -z $(DESTDIR)$(SRV_LOGDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_LOGDIR)
test -z $(DESTDIR)$(SRV_RUNDIR) || $(MKDIR_P) $(DESTDIR)$(SRV_RUNDIR)
test -z $(DESTDIR)$(SRV_DATADIR) || $(MKDIR_P) $(DESTDIR)$(SRV_DATADIR)
env CGO_ENABLED=1 $(GO) run $(GOFLAGS) ./cmd/mbased/... --daemon=false
distclean-local: clean
rm -rf autom4te.cache
clean-local:
rm -f */*/*~
rm -f */*~
rm -f *~
rm -f cmd/mbaseadmin/mbaseadmin
rm -f cmd/mbasectl/mbasectl
rm -f cmd/mbased/mbased
rm -f cmd/mbasedump/mbasedump
rm -f cmd/mbaselocal/mbaselocal
rm -f cmd/mbaserestore/mbaserestore
rm -rf autom4te.cache
rm -rf tmp/
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
# Tell GNU make to disable its built-in pattern rules.
%:: %,v
%:: RCS/%,v
%:: RCS/%
%:: s.%
%:: SCCS/s.%
View File
Vendored
+1307
View File
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
package config
import (
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"mbase/pkg/client"
"go.yaml.in/yaml/v4"
)
const (
defaultHostname = "localhost"
configFilename = "maind.yaml"
logFilename = "maind.log"
pidFilename = "maind.pid"
)
var (
buildVersion = "NONE"
)
type Networks struct {
Enabled []string `json:"enabled" yaml:"enabled"`
Disabled []string `json:"disabled" yaml:"disabled"`
}
type ServiceConfig struct {
Portnum uint32 `json:"port" yaml:"port"`
Address string `json:"address" yaml:"address"`
Protocol string `json:"protocol" yaml:"protocol"`
}
type Config struct {
PackageVersion string `json:"packageVersion" yaml:"packageVersion"`
Service ServiceConfig `json:"service" yaml:"service"`
Networks Networks `json:"networks" yaml:"networks"`
Hostname string `json:"hostname" yaml:"hostname"`
Debug bool `json:"debug" yaml:"debug"`
Build string `json:"build" yaml:"build"`
LogPath string `json:"logfile" yaml:"logfile"`
RunPath string `json:"runfile" yaml:"runfile"`
DataDir string `json:"datadir" yaml:"datadir"`
Daemon bool `json:"daemon" yaml:"daemon"`
}
var (
defaultEnabledNetworks = []string{"0.0.0.0/0", "::/0"}
defaultDisabledNetworks = []string{}
)
const (
defaultServiceAddress = "0.0.0.0"
defaultServiceProtocol = "tcp"
)
func NewConfig() *Config {
conf := &Config{
Service: ServiceConfig{
Portnum: client.DefaultPort,
Address: defaultServiceAddress,
Protocol: defaultServiceProtocol,
},
DataDir: datadirPath,
Debug: false,
Hostname: defaultHostname,
Build: buildVersion,
Daemon: true,
PackageVersion: packageVersion,
Networks: Networks{
Enabled: defaultEnabledNetworks,
Disabled: defaultDisabledNetworks,
},
}
conf.LogPath = filepath.Join(logdirPath, logFilename)
conf.RunPath = filepath.Join(rundirPath, pidFilename)
return conf
}
func (conf *Config) ReadFile() error {
var err error
confPath := filepath.Join(confdirPath, configFilename)
confBytes, err := os.ReadFile(confPath)
if err != nil {
return err
}
err = yaml.Unmarshal(confBytes, conf)
if err != nil {
return err
}
conf.Normalize()
err = conf.Validate()
if err != nil {
return err
}
return nil
}
func (conf *Config) ReadEnv() error {
var err error
return err
}
func (conf *Config) ReadOpts() error {
var err error
exeName := filepath.Base(os.Args[0])
flag.BoolVar(&conf.Daemon, "daemon", conf.Daemon, "run as daemon")
flag.BoolVar(&conf.Debug, "debug", conf.Debug, "on debug mode")
help := func() {
fmt.Println("")
fmt.Printf("Usage: %s [option]\n", exeName)
fmt.Println("")
fmt.Println("Options:")
flag.PrintDefaults()
fmt.Println("")
}
flag.Usage = help
flag.Parse()
return err
}
func (conf *Config) String() (string, error) {
var err error
var res string
yamlBytes, err := yaml.Marshal(conf)
if err != nil {
return res, err
}
res = string(yamlBytes)
return res, err
}
func (conf *Config) Normalize() {
if conf.Service.Portnum == 0 {
conf.Service.Portnum = client.DefaultPort
}
}
func (conf *Config) Validate() error {
var err []error
return errors.Join(err...)
}
+10
View File
@@ -0,0 +1,10 @@
package config
const (
confdirPath = "@srv_confdir@"
rundirPath = "@srv_rundir@"
logdirPath = "@srv_logdir@"
datadirPath = "@srv_datadir@"
packageVersion = "@PACKAGE_VERSION@"
)
+114
View File
@@ -0,0 +1,114 @@
package database
import (
"context"
"mbase/app/descriptor"
)
func (db *Database) InsertAccount(ctx context.Context, account *descriptor.Account) error {
var err error
request := `INSERT INTO account(id, username, passhash, disabled, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6)`
_, err = db.db.Exec(request, account.ID, account.Username, account.Passhash,
account.Disabled, account.CreatedAt, account.UpdatedAt)
if err != nil {
return err
}
return err
}
func (db *Database) UpdateAccountByID(ctx context.Context, accountID int64, account *descriptor.Account) error {
var err error
request := `UPDATE account SET username = $1, passhash = $2, disabled = $3, updated_at = $4 WHERE id = $5`
_, err = db.db.Exec(request, account.Username, account.Passhash, account.Disabled, account.UpdatedAt, accountID)
if err != nil {
return err
}
return err
}
func (db *Database) ReducedListAccounts(ctx context.Context) ([]descriptor.Account, error) {
var err error
request := `SELECT id, username, disabled, created_at, updated_at FROM account`
res := make([]descriptor.Account, 0)
err = db.db.Select(&res, request)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) CompletedListAccounts(ctx context.Context) ([]descriptor.Account, error) {
var err error
request := `SELECT * FROM account`
res := make([]descriptor.Account, 0)
err = db.db.Select(&res, request)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) GetAccountByID(ctx context.Context, accountID int64) (bool, *descriptor.Account, error) {
var err error
var res *descriptor.Account
var exists bool
request := `SELECT id, username, passhash, disabled, created_at, updated_at
FROM account WHERE id = $1 LiMIT 1`
dbRes := make([]descriptor.Account, 0)
err = db.db.Select(&dbRes, request, accountID)
if err != nil {
return exists, res, err
}
if len(dbRes) == 0 {
return exists, res, err
}
exists = true
res = &dbRes[0]
return exists, res, err
}
func (db *Database) GetAccountByUsername(ctx context.Context, username string) (bool, *descriptor.Account, error) {
var err error
var res *descriptor.Account
var exists bool
request := `SELECT id, username, passhash, disabled, created_at, updated_at
FROM account WHERE username = $1 LIMIT 1`
dbRes := make([]descriptor.Account, 0)
err = db.db.Select(&dbRes, request, username)
if err != nil {
return exists, res, err
}
if len(dbRes) == 0 {
return false, res, err
}
exists = true
res = &dbRes[0]
return exists, res, err
}
func (db *Database) DeleteAccountByID(ctx context.Context, accountID int64) error {
var err error
request := `DELETE FROM account WHERE id = $1`
_, err = db.db.Exec(request, accountID)
if err != nil {
return err
}
return err
}
func (db *Database) DeleteAccountByUsername(ctx context.Context, username string) error {
var err error
request := `DELETE FROM account WHERE username = $1`
_, err = db.db.Exec(request, username)
if err != nil {
return err
}
return err
}
+124
View File
@@ -0,0 +1,124 @@
package database
import (
"context"
"path/filepath"
"mbase/pkg/logger"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
const schema = `
--- DROP TABLE IF EXISTS issuer;
CREATE TABLE IF NOT EXISTS issuer (
id INT NOT NULL,
name TEXT NOT NULL,
cert TEXT NOT NULL,
key TEXT,
signer_id INT NOT NULL,
signer_name TEXT NOT NULL,
revoked BOOL
);
CREATE UNIQUE INDEX IF NOT EXISTS issuer_index01
ON issuer(id);
CREATE UNIQUE INDEX IF NOT EXISTS issuer_index02
ON issuer(name);
--- DROP TABLE IF EXISTS service;
CREATE TABLE IF NOT EXISTS service (
id INT NOT NULL,
issuer_id INT NOT NULL,
issuer_name TEXT NOT NULL,
name TEXT NOT NULL,
cert TEXT NOT NULL,
key TEXT NOT NULL,
revoked BOOL
);
CREATE UNIQUE INDEX IF NOT EXISTS service_index01
ON service(id);
CREATE UNIQUE INDEX IF NOT EXISTS service_index02
ON service(name);
--- DROP TABLE IF EXISTS account;
CREATE TABLE IF NOT EXISTS account (
id INT NOT NULL,
username TEXT NOT NULL,
passhash TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
disabled BOOL
);
CREATE UNIQUE INDEX IF NOT EXISTS account_index01
ON account(id);
CREATE UNIQUE INDEX IF NOT EXISTS account_index02
ON account(username);
--- DROP TABLE IF EXISTS grant;
CREATE TABLE IF NOT EXISTS grant (
id INT NOT NULL,
account_id INT NOT NULL,
operation TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS grant_index01
ON grant(account_id);
CREATE UNIQUE INDEX IF NOT EXISTS grant_index02
ON grant(account_id, operation);
`
type Database struct {
datapath string
db *sqlx.DB
log *logger.Logger
}
func NewDatabase(datapath string) (*Database, error) {
var err error
db := &Database{
datapath: datapath,
}
db.log = logger.NewLogger("database")
return db, err
}
func (db *Database) OpenDatabase() error {
var err error
dbPath := filepath.Join(db.datapath, "certmanager.db")
db.db, err = sqlx.Open("sqlite3", dbPath)
if err != nil {
return err
}
err = db.db.Ping()
if err != nil {
return err
}
return err
}
func (db *Database) InitDatabase() error {
var err error
_, err = db.db.Exec(schema)
if err != nil {
return err
}
return err
}
func (db *Database) CleanDatabase(ctx context.Context) error {
var err error
request := `
DELETE FROM account;
DELETE FROM grant;
`
_, err = db.db.Exec(request)
if err != nil {
return err
}
return err
}
+77
View File
@@ -0,0 +1,77 @@
package database
import (
"context"
"mbase/app/descriptor"
)
func (db *Database) InsertGrant(ctx context.Context, grant *descriptor.Grant) error {
var err error
request := `INSERT INTO grant(id, account_id, operation, created_at)
VALUES ($1, $2, $3, $4)`
_, err = db.db.Exec(request, grant.ID, grant.AccountID, grant.Operation, grant.CreatedAt)
if err != nil {
return err
}
return err
}
func (db *Database) ListGrantsByAccountID(ctx context.Context, accountID int64) ([]descriptor.Grant, error) {
var err error
request := `SELECT * FROM grant WHERE account_id = $1`
res := make([]descriptor.Grant, 0)
err = db.db.Select(&res, request, accountID)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) ListGrants(ctx context.Context) ([]descriptor.Grant, error) {
var err error
request := `SELECT * FROM grant`
res := make([]descriptor.Grant, 0)
err = db.db.Select(&res, request)
if err != nil {
return res, err
}
return res, err
}
func (db *Database) GetGrant(ctx context.Context, accountID int64, operation string) (bool, *descriptor.Grant, error) {
var err error
res := &descriptor.Grant{}
request := `SELECT * FROM grant WHERE account_id = $1 AND operation = $2 LIMIT 1`
dbRes := make([]descriptor.Grant, 0)
err = db.db.Select(&dbRes, request, accountID, operation)
if err != nil {
return false, res, err
}
if len(dbRes) == 0 {
return false, res, err
}
res = &dbRes[0]
return true, res, err
}
func (db *Database) DeleteGrantByAccountID(ctx context.Context, grantID int64, operation string) error {
var err error
request := `DELETE FROM grant WHERE account_id = $1 AND operation = $2`
_, err = db.db.Exec(request, grantID, operation)
if err != nil {
return err
}
return err
}
func (db *Database) DeleteAllGrantsForAccountID(ctx context.Context, grantID int64) error {
var err error
request := `DELETE FROM grant WHERE account_id = $1`
_, err = db.db.Exec(request, grantID)
if err != nil {
return err
}
return err
}
+34
View File
@@ -0,0 +1,34 @@
package descriptor
const (
GrantModifyUsers = "modifyUsers"
GrantModifyDatabase = "modifyDatabase"
)
type Dump struct {
Timestamp string `json:"timestamp" yaml:"timestamp"`
Accounts []Account `json:"accounts" yaml:"accounts"`
Grants []Grant `json:"grants" yaml:"grants"`
}
type Account struct {
ID int64 `json:"id" yaml:"id" db:"id"`
Username string `json:"username" yaml:"username" db:"username"`
Passhash string `json:"passhash" yaml:"passhash" db:"passhash"`
Disabled bool `json:"disabled" yaml:"disabled" db:"disabled"`
CreatedAt string `json:"createdAt" yaml:"createdAt" db:"created_at"`
UpdatedAt string `json:"updatedAt" yaml:"updatedAt" db:"updated_at"`
}
type Grant struct {
ID int64 `json:"id" yaml:"id" db:"id"`
AccountID int64 `json:"accountID" yaml:"accountID" db:"account_id"`
Operation string `json:"operation" yaml:"operation" db:"operation"`
CreatedAt string `json:"createdAt" yaml:"createdAt" db:"created_at"`
}
type Server struct {
DatabaseInitialized bool `json:"databaseInitialized" yaml:"databaseInitialized"`
CreatedAt string `json:"createdAt" yaml:"createdAt"`
UpdatedAt string `json:"updatedAt" yaml:"updatedAt"`
}
+80
View File
@@ -0,0 +1,80 @@
package handler
import (
"context"
"mbase/pkg/mbctl"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func (hand *Handler) Authentificate(ctx context.Context) (int64, error) {
var err error
var accountID int64
meta, _ := metadata.FromIncomingContext(ctx)
usernameArr := meta["username"]
passwordArr := meta["password"]
if len(usernameArr) == 0 || len(passwordArr) == 0 {
err := status.Errorf(codes.PermissionDenied, "Empty auth data")
return accountID, err
}
username := meta["username"][0]
password := meta["password"][0]
validated, accountID, err := hand.lg.ValidateAcount(ctx, username, password)
if !validated {
err := status.Errorf(codes.PermissionDenied, "Wrong auth data")
return accountID, err
}
return accountID, err
}
func (hand *Handler) CreateAccount(ctx context.Context, params *mbctl.CreateAccountParams) (*mbctl.CreateAccountResult, error) {
var err error
hand.log.Debugf("Handle CreateAccount call")
res := &mbctl.CreateAccountResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.CreateAccount(ctx, accountID, params)
return res, err
}
func (hand *Handler) DeleteAccount(ctx context.Context, params *mbctl.DeleteAccountParams) (*mbctl.DeleteAccountResult, error) {
var err error
hand.log.Debugf("Handle DeleteAccount call")
res := &mbctl.DeleteAccountResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.DeleteAccount(ctx, accountID, params)
return res, err
}
func (hand *Handler) ListAccounts(ctx context.Context, params *mbctl.ListAccountsParams) (*mbctl.ListAccountsResult, error) {
var err error
hand.log.Debugf("Handle ListAccounts call")
res := &mbctl.ListAccountsResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.ListAccounts(ctx, accountID, params)
return res, err
}
func (hand *Handler) UpdateAccount(ctx context.Context, params *mbctl.UpdateAccountParams) (*mbctl.UpdateAccountResult, error) {
var err error
hand.log.Debugf("Handle UpdateAccount call")
res := &mbctl.UpdateAccountResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.UpdateAccount(ctx, accountID, params)
return res, err
}
+31
View File
@@ -0,0 +1,31 @@
package handler
import (
"context"
"mbase/pkg/mbctl"
)
func (hand *Handler) GetDump(ctx context.Context, params *mbctl.GetDumpParams) (*mbctl.GetDumpResult, error) {
var err error
hand.log.Debugf("Handle GetDump call")
res := &mbctl.GetDumpResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.GetDump(ctx, accountID, params)
return res, err
}
func (hand *Handler) RestoreDump(ctx context.Context, params *mbctl.RestoreDumpParams) (*mbctl.RestoreDumpResult, error) {
var err error
hand.log.Debugf("Handle GetDump call")
res := &mbctl.RestoreDumpResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.RestoreDump(ctx, accountID, params)
return res, err
}
+31
View File
@@ -0,0 +1,31 @@
package handler
import (
"context"
"mbase/pkg/mbctl"
)
func (hand *Handler) SetGrant(ctx context.Context, params *mbctl.SetGrantParams) (*mbctl.SetGrantResult, error) {
var err error
hand.log.Debugf("Handle SetGrant call")
res := &mbctl.SetGrantResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.SetGrant(ctx, accountID, params)
return res, err
}
func (hand *Handler) DeleteGrant(ctx context.Context, params *mbctl.DeleteGrantParams) (*mbctl.DeleteGrantResult, error) {
var err error
hand.log.Debugf("Handle DeleteGrant call")
res := &mbctl.DeleteGrantResult{}
accountID, err := hand.Authentificate(ctx)
if err != nil {
return res, err
}
res, err = hand.lg.DeleteGrant(ctx, accountID, params)
return res, err
}
+31
View File
@@ -0,0 +1,31 @@
package handler
import (
"mbase/app/logic"
"mbase/pkg/mbctl"
"mbase/pkg/logger"
"google.golang.org/grpc"
)
type HandlerConfig struct {
Logic *logic.Logic
}
type Handler struct {
mbctl.UnimplementedControlServer
lg *logic.Logic
log *logger.Logger
}
func NewHandler(conf *HandlerConfig) *Handler {
hand := Handler{
lg: conf.Logic,
}
hand.log = logger.NewLogger("ghandler")
return &hand
}
func (hand *Handler) Register(gsrv *grpc.Server) {
mbctl.RegisterControlServer(gsrv, hand)
}
+14
View File
@@ -0,0 +1,14 @@
package handler
import (
"context"
"mbase/pkg/mbctl"
)
func (hand *Handler) GetHello(ctx context.Context, params *mbctl.GetHelloParams) (*mbctl.GetHelloResult, error) {
var err error
hand.log.Debugf("Handle getHello call")
res, err := hand.lg.GetHello(ctx, params)
return res, err
}
+232
View File
@@ -0,0 +1,232 @@
package logic
import (
"context"
"fmt"
"time"
"mbase/app/descriptor"
"mbase/pkg/auxid"
"mbase/pkg/auxpwd"
"mbase/pkg/mbctl"
)
func (lg *Logic) ValidateAcount(ctx context.Context, username, password string) (bool, int64, error) {
var err error
var accountID int64
var valid bool
lg.WaitRestoring()
accountExists, accountDescr, err := lg.db.GetAccountByUsername(ctx, username)
if !accountExists {
err := fmt.Errorf("Account not exists")
return valid, accountID, err
}
if !auxpwd.PasswordMatchCompat([]byte(password), accountDescr.Passhash) {
err := fmt.Errorf("Login data mismatch")
return valid, accountID, err
}
valid = true
accountID = accountDescr.ID
return valid, accountID, err
}
func (lg *Logic) CreateAccount(ctx context.Context, accountID int64, params *mbctl.CreateAccountParams) (*mbctl.CreateAccountResult, error) {
var err error
res := &mbctl.CreateAccountResult{}
lg.WaitDumping()
lg.WaitRestoring()
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyUsers)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
if params.Username == "" {
err := fmt.Errorf("Empty username parameters")
return res, err
}
if params.Password == "" {
err := fmt.Errorf("Empty password parameter")
return res, err
}
accountExists, _, err := lg.db.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
if accountExists {
err := fmt.Errorf("Account with thist name already exists")
return res, err
}
now := time.Now().Format(time.RFC3339)
passhash := auxpwd.MakeSHA256Hash([]byte(params.Password))
accountDescr := &descriptor.Account{
ID: auxid.GenID(),
Username: params.Username,
Passhash: passhash,
Disabled: false,
CreatedAt: now,
UpdatedAt: now,
}
err = lg.db.InsertAccount(ctx, accountDescr)
if err != nil {
return res, err
}
res.AccountID = accountDescr.ID
return res, err
}
func (lg *Logic) UpdateAccount(ctx context.Context, accountID int64, params *mbctl.UpdateAccountParams) (*mbctl.UpdateAccountResult, error) {
var err error
res := &mbctl.UpdateAccountResult{}
lg.WaitRestoring()
lg.WaitDumping()
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyUsers)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
var accountDescr *descriptor.Account
var accountExists bool
switch {
case params.AccountID != 0:
accountExists, accountDescr, err = lg.db.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = lg.db.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
}
if !accountExists {
err := fmt.Errorf("Account with this is or name dont exists")
return res, err
}
now := time.Now().Format(time.RFC3339)
if params.NewUsername != "" {
accountDescr.UpdatedAt = now
accountDescr.Username = params.NewUsername
}
if params.NewPassword != "" {
passhash := auxpwd.MakeSHA256Hash([]byte(params.NewPassword))
accountDescr.UpdatedAt = now
accountDescr.Passhash = passhash
}
if params.Disabled != accountDescr.Disabled {
accountDescr.UpdatedAt = now
accountDescr.Disabled = params.Disabled
}
err = lg.db.UpdateAccountByID(ctx, accountDescr.ID, accountDescr)
if err != nil {
return res, err
}
return res, err
}
func (lg *Logic) DeleteAccount(ctx context.Context, accountID int64, params *mbctl.DeleteAccountParams) (*mbctl.DeleteAccountResult, error) {
var err error
res := &mbctl.DeleteAccountResult{}
lg.WaitDumping()
lg.WaitRestoring()
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyUsers)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
var accountDescr *descriptor.Account
var accountExists bool
switch {
case params.AccountID != 0:
accountExists, accountDescr, err = lg.db.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = lg.db.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
}
if !accountExists {
err := fmt.Errorf("Account with this is or name dont exists")
return res, err
}
err = lg.db.DeleteAllGrantsForAccountID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
err = lg.db.DeleteAccountByID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
return res, err
}
func (lg *Logic) ListAccounts(ctx context.Context, accountID int64, params *mbctl.ListAccountsParams) (*mbctl.ListAccountsResult, error) {
var err error
res := &mbctl.ListAccountsResult{
Accounts: make([]*mbctl.AccountShortDescr, 0),
}
lg.WaitRestoring()
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyUsers)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
accountDescrs, err := lg.db.ReducedListAccounts(ctx)
if err != nil {
return res, err
}
for _, accountDescr := range accountDescrs {
accountShortDescr := &mbctl.AccountShortDescr{
Username: accountDescr.Username,
Disabled: accountDescr.Disabled,
CreatedAt: accountDescr.CreatedAt,
UpdatedAt: accountDescr.UpdatedAt,
Grants: make([]*mbctl.GrantShortDescr, 0),
}
grantDescrs, err := lg.db.ListGrantsByAccountID(ctx, accountDescr.ID)
if err != nil {
return res, err
}
for _, grantDescrs := range grantDescrs {
grantShortDescrs := &mbctl.GrantShortDescr{
Operation: grantDescrs.Operation,
CreatedAt: grantDescrs.CreatedAt,
}
accountShortDescr.Grants = append(accountShortDescr.Grants, grantShortDescrs)
}
res.Accounts = append(res.Accounts, accountShortDescr)
}
return res, err
}
+71
View File
@@ -0,0 +1,71 @@
package logic
import (
"context"
"time"
"mbase/app/descriptor"
"mbase/pkg/auxid"
"mbase/pkg/auxpwd"
)
const (
defaultSeedUsername = "certman"
defaultSeedPassword = "certman"
)
func (lg *Logic) CleanDatabase(ctx context.Context) error {
var err error
err = lg.db.CleanDatabase(ctx)
if err != nil {
return err
}
return err
}
func (lg *Logic) SeedAccount(ctx context.Context) (int64, error) {
var err error
var accountID int64
accountDescrs, err := lg.db.ReducedListAccounts(ctx)
if err != nil {
return accountID, err
}
lg.log.Debugf("Seed account")
if len(accountDescrs) == 0 {
now := time.Now().Format(time.RFC3339)
passhash := auxpwd.MakeSHA256Hash([]byte(defaultSeedPassword))
accountDescr := &descriptor.Account{
ID: auxid.GenID(),
Username: defaultSeedUsername,
Passhash: passhash,
Disabled: false,
CreatedAt: now,
UpdatedAt: now,
}
err = lg.db.InsertAccount(ctx, accountDescr)
if err != nil {
return accountID, err
}
accountID = accountDescr.ID
grantTypes := []string{
descriptor.GrantModifyUsers,
descriptor.GrantModifyDatabase,
}
for _, grantType := range grantTypes {
grantDescr := &descriptor.Grant{
AccountID: accountDescr.ID,
Operation: grantType,
CreatedAt: now,
}
err = lg.db.InsertGrant(ctx, grantDescr)
if err != nil {
return accountID, err
}
}
}
return accountID, err
}
+107
View File
@@ -0,0 +1,107 @@
package logic
import (
"context"
"fmt"
"time"
"mbase/app/descriptor"
"mbase/pkg/mbctl"
"go.yaml.in/yaml/v4"
)
func (lg *Logic) GetDump(ctx context.Context, accountID int64, params *mbctl.GetDumpParams) (*mbctl.GetDumpResult, error) {
var err error
res := &mbctl.GetDumpResult{}
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyDatabase)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
lg.WaitRestoring()
lg.WaitDumping()
lg.DumpingSemUp()
defer lg.DumpingSemDown()
listAccounts, err := lg.db.CompletedListAccounts(ctx)
if err != nil {
return res, err
}
listGrants, err := lg.db.ListGrants(ctx)
if err != nil {
return res, err
}
lg.DumpingSemDown()
dump := descriptor.Dump{
Timestamp: time.Now().Format(time.RFC3339),
Accounts: listAccounts,
Grants: listGrants,
}
dumpBytes, err := yaml.Marshal(dump)
if err != nil {
return res, err
}
res.Dump = string(dumpBytes)
return res, err
}
func (lg *Logic) RestoreDump(ctx context.Context, accountID int64, params *mbctl.RestoreDumpParams) (*mbctl.RestoreDumpResult, error) {
var err error
res := &mbctl.RestoreDumpResult{}
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyDatabase)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
lg.WaitDumping()
lg.WaitRestoring()
var dump descriptor.Dump
err = yaml.Unmarshal([]byte(params.Dump), &dump)
if err != nil {
return res, err
}
lg.RestoringSemUp()
defer lg.RestoringSemDown()
if params.DeleteAllRecords {
err = lg.db.CleanDatabase(ctx)
if err != nil {
return res, err
}
}
for _, account := range dump.Accounts {
lg.log.Infof("Insert account %s", account.Username)
err = lg.db.InsertAccount(ctx, &account)
if err != nil {
lg.log.Errorf("Insert account error: %v", err)
}
}
for _, grant := range dump.Grants {
lg.log.Infof("Insert grant %s for account %d", grant.Operation, grant.AccountID)
err = lg.db.InsertGrant(ctx, &grant)
if err != nil {
lg.log.Errorf("Insert account error: %v", err)
}
}
return res, err
}
+148
View File
@@ -0,0 +1,148 @@
package logic
import (
"context"
"fmt"
"time"
"mbase/app/descriptor"
"mbase/pkg/auxid"
"mbase/pkg/mbctl"
)
func (lg *Logic) SetGrant(ctx context.Context, accountID int64, params *mbctl.SetGrantParams) (*mbctl.SetGrantResult, error) {
var err error
res := &mbctl.SetGrantResult{}
lg.WaitDumping()
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyUsers)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
grantTypes := []string{
descriptor.GrantModifyUsers,
descriptor.GrantModifyDatabase,
}
var grantOk bool
for _, grantType := range grantTypes {
if grantType == params.Operation {
grantOk = true
break
}
}
if !grantOk {
err := fmt.Errorf("Unknown grant type")
return res, err
}
var accountDescr *descriptor.Account
var accountExists bool
switch {
case params.AccountID != 0:
accountExists, accountDescr, err = lg.db.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = lg.db.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
}
if !accountExists || accountDescr == nil {
err := fmt.Errorf("Account with this id or name dont exists")
return res, err
}
grantExists, _, err = lg.db.GetGrant(ctx, accountDescr.ID, params.Operation)
if err != nil {
return res, err
}
if grantExists {
err := fmt.Errorf("Grant %s for the user already exists", params.Operation)
return res, err
}
now := time.Now().Format(time.RFC3339)
grantDescr := &descriptor.Grant{
ID: auxid.GenID(),
AccountID: accountDescr.ID,
CreatedAt: now,
Operation: params.Operation,
}
err = lg.db.InsertGrant(ctx, grantDescr)
if err != nil {
return res, err
}
return res, err
}
func (lg *Logic) DeleteGrant(ctx context.Context, accountID int64, params *mbctl.DeleteGrantParams) (*mbctl.DeleteGrantResult, error) {
var err error
res := &mbctl.DeleteGrantResult{}
lg.WaitDumping()
grantExists, _, err := lg.db.GetGrant(ctx, accountID, descriptor.GrantModifyUsers)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Operation not allowed for the user")
return res, err
}
grantTypes := []string{
descriptor.GrantModifyUsers,
descriptor.GrantModifyDatabase,
}
var grantOk bool
for _, grantType := range grantTypes {
if grantType == params.Operation {
grantOk = true
break
}
}
if !grantOk {
err := fmt.Errorf("Unknown grant type")
return res, err
}
var accountDescr *descriptor.Account
var accountExists bool
switch {
case params.AccountID != 0:
accountExists, accountDescr, err = lg.db.GetAccountByID(ctx, params.AccountID)
if err != nil {
return res, err
}
case params.Username != "":
accountExists, accountDescr, err = lg.db.GetAccountByUsername(ctx, params.Username)
if err != nil {
return res, err
}
}
if !accountExists || accountDescr == nil {
err := fmt.Errorf("Account with this id or name dont exists")
return res, err
}
grantExists, _, err = lg.db.GetGrant(ctx, accountDescr.ID, params.Operation)
if err != nil {
return res, err
}
if !grantExists {
err := fmt.Errorf("Requested grant for the user not found")
return res, err
}
err = lg.db.DeleteGrantByAccountID(ctx, accountDescr.ID, params.Operation)
if err != nil {
return res, err
}
return res, err
}
+15
View File
@@ -0,0 +1,15 @@
package logic
import (
"context"
"mbase/pkg/mbctl"
)
func (lg *Logic) GetHello(ctx context.Context, params *mbctl.GetHelloParams) (*mbctl.GetHelloResult, error) {
var err error
res := &mbctl.GetHelloResult{
Message: "hello",
}
return res, err
}
+63
View File
@@ -0,0 +1,63 @@
package logic
import (
"sync/atomic"
"time"
"mbase/app/database"
"mbase/pkg/logger"
)
type LogicConfig struct {
Database *database.Database
}
type Logic struct {
log *logger.Logger
db *database.Database
dumpingSem atomic.Bool
restoringSem atomic.Bool
}
func NewLogic(conf *LogicConfig) (*Logic, error) {
var err error
lg := &Logic{
db: conf.Database,
}
lg.log = logger.NewLogger("logic")
return lg, err
}
func (lg *Logic) DumpingSemUp() {
lg.dumpingSem.Store(true)
}
func (lg *Logic) DumpingSemDown() {
lg.dumpingSem.Store(false)
}
func (lg *Logic) WaitDumping() {
for {
if !lg.dumpingSem.Load() {
return
}
time.Sleep(1 * time.Millisecond)
}
}
func (lg *Logic) RestoringSemUp() {
lg.restoringSem.Store(true)
}
func (lg *Logic) RestoringSemDown() {
lg.restoringSem.Store(false)
}
func (lg *Logic) WaitRestoring() {
for {
if !lg.restoringSem.Load() {
return
}
time.Sleep(1 * time.Millisecond)
}
}
+360
View File
@@ -0,0 +1,360 @@
package server
import (
"io/ioutil"
"os"
"os/signal"
"os/user"
"path/filepath"
"strconv"
"syscall"
"time"
"mbase/app/config"
"mbase/app/database"
"mbase/app/descriptor"
"mbase/app/logic"
"mbase/pkg/aux509"
"mbase/pkg/logger"
"mbase/pkg/netacl"
handler "mbase/app/handler"
service "mbase/app/service"
"sigs.k8s.io/yaml"
)
type Server struct {
conf *config.Config
lg *logic.Logic
svc *service.Service
hand *handler.Handler
log *logger.Logger
nacl *netacl.NetACL
db *database.Database
x509cert []byte
x509key []byte
state descriptor.Server
sfile string
}
func NewServer() (*Server, error) {
var err error
srv := &Server{}
srv.log = logger.NewLogger("server")
return srv, err
}
func (srv *Server) Configure() error {
var err error
srv.conf = config.NewConfig()
err = srv.conf.ReadFile()
if err != nil {
return err
}
err = srv.conf.ReadEnv()
if err != nil {
return err
}
err = srv.conf.ReadOpts()
if err != nil {
return err
}
srv.sfile = filepath.Join(srv.conf.DataDir, "certmanager.yaml")
return err
}
func (srv *Server) LoadState() error {
var err error
_, err = os.Stat(srv.sfile)
if os.IsNotExist(err) {
err = nil
return err
}
file, err := os.Open(srv.sfile)
if err != nil {
return err
}
defer file.Close()
stateBytes, err := ioutil.ReadAll(file)
if err != nil {
return err
}
err = yaml.Unmarshal(stateBytes, &srv.state)
if err != nil {
return err
}
return err
}
func (srv *Server) SaveState() error {
var err error
file, err := os.OpenFile(srv.sfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
srv.state.UpdatedAt = time.Now().Format(time.RFC3339)
if srv.state.CreatedAt == "" {
srv.state.CreatedAt = srv.state.UpdatedAt
}
stateBytes, err := yaml.Marshal(srv.state)
if err != nil {
return err
}
_, err = file.Write(stateBytes)
if err != nil {
return err
}
return err
}
func (srv *Server) Build() error {
var err error
srv.log.Infof("Build server")
// Mkdir log and data dir
srv.log.Infof("Create %s dir", srv.conf.DataDir)
err = os.MkdirAll(srv.conf.DataDir, 0750)
if err != nil {
return err
}
if srv.conf.Daemon {
logDir := filepath.Dir(srv.conf.LogPath)
srv.log.Infof("Create %s dir", logDir)
err = os.MkdirAll(logDir, 0750)
if err != nil {
return err
}
runDir := filepath.Dir(srv.conf.RunPath)
srv.log.Infof("Create %s dir", runDir)
err = os.MkdirAll(runDir, 0750)
if err != nil {
return err
}
}
// Create X509 certs
srv.x509cert, srv.x509key, err = aux509.CreateX509SelfSignedCert(srv.conf.Hostname)
if err != nil {
return err
}
// Load state
err = srv.LoadState()
if err != nil {
return err
}
// Create netACL
err = srv.nacl.AddDisabledAddresses(srv.conf.Networks.Disabled...)
if err != nil {
return err
}
srv.nacl = netacl.NewNetACL()
err = srv.nacl.AddEnabledAddresses(srv.conf.Networks.Enabled...)
if err != nil {
return err
}
naclYAML, err := yaml.Marshal(srv.nacl)
if err != nil {
return err
}
srv.log.Infof("Network ACL is:\n%s\n", string(naclYAML))
// Create database
srv.db, err = database.NewDatabase(srv.conf.DataDir)
err = srv.db.OpenDatabase()
if err != nil {
return err
}
// Load state
err = srv.LoadState()
if err != nil {
return err
}
// Create logic
logicConfig := &logic.LogicConfig{
Database: srv.db,
}
srv.lg, err = logic.NewLogic(logicConfig)
if err != nil {
return err
}
if !srv.state.DatabaseInitialized {
// Create schema
srv.log.Infof("Init database")
err = srv.db.InitDatabase()
if err != nil {
return err
}
srv.state.DatabaseInitialized = true
err = srv.SaveState()
if err != nil {
return err
}
}
// Create handler
handlerConfig := &handler.HandlerConfig{
Logic: srv.lg,
}
srv.hand = handler.NewHandler(handlerConfig)
if err != nil {
return err
}
// Create service
serviceConfig := &service.ServiceConfig{
Portnum: srv.conf.Service.Portnum,
Address: srv.conf.Service.Address,
Protocol: srv.conf.Service.Protocol,
Hostname: srv.conf.Hostname,
Handler: srv.hand,
Logic: srv.lg,
X509Cert: srv.x509cert,
X509Key: srv.x509key,
NetACL: srv.nacl,
}
srv.svc = service.NewService(serviceConfig)
if err != nil {
return err
}
return err
}
func (srv *Server) Run() error {
var err error
// Log configuration
yamlConfig, err := srv.conf.String()
if err != nil {
return err
}
srv.log.Debugf("Server configuration:\n%s\n", yamlConfig)
// Show current user
currUser, err := user.Current()
if err != nil {
return err
}
srv.log.Infof("Running server as user %s", currUser.Username)
sigs := make(chan os.Signal, 1)
done := make(chan error, 1)
// Run service
startService := func(svc *service.Service, done chan error) {
err = svc.Run()
if err != nil {
srv.log.Errorf("Service error: %v", err)
done <- err
}
}
go startService(srv.svc, done)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
var signal os.Signal
select {
case signal = <-sigs:
srv.log.Infof("Services stopped by signal: %v", signal)
srv.svc.Stop()
}
return err
}
func (srv *Server) PseudoFork() error {
const successExit int = 0
var keyEnv string = "IMX0LTSELMRF8KASWER"
var err error
_, isChild := os.LookupEnv(keyEnv)
switch {
case !isChild:
os.Setenv(keyEnv, "TRUE")
procAttr := syscall.ProcAttr{}
cwd, err := os.Getwd()
if err != nil {
return err
}
var sysFiles = make([]uintptr, 3)
sysFiles[0] = uintptr(syscall.Stdin)
sysFiles[1] = uintptr(syscall.Stdout)
sysFiles[2] = uintptr(syscall.Stderr)
procAttr.Files = sysFiles
procAttr.Env = os.Environ()
procAttr.Dir = cwd
_, err = syscall.ForkExec(os.Args[0], os.Args, &procAttr)
if err != nil {
return err
}
os.Exit(successExit)
case isChild:
_, err = syscall.Setsid()
if err != nil {
return err
}
}
os.Unsetenv(keyEnv)
return err
}
func (srv *Server) Daemonize() error {
var err error
if srv.conf.Daemon {
// Restart process process
err = srv.PseudoFork()
if err != nil {
return err
}
// Redirect stdin
nullFile, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err != nil {
return err
}
err = syscall.Dup2(int(nullFile.Fd()), int(os.Stdin.Fd()))
if err != nil {
return err
}
// Redirect stderr and stout
logdir := filepath.Dir(srv.conf.LogPath)
err = os.MkdirAll(logdir, 0750)
if err != nil {
return err
}
logFile, err := os.OpenFile(srv.conf.LogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
if err != nil {
return err
}
err = syscall.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))
if err != nil {
return err
}
err = syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
return err
}
// Write process ID
rundir := filepath.Dir(srv.conf.RunPath)
err = os.MkdirAll(rundir, 0750)
if err != nil {
return err
}
pidFile, err := os.OpenFile(srv.conf.RunPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer pidFile.Close()
currPid := os.Getpid()
_, err = pidFile.WriteString(strconv.Itoa(currPid))
if err != nil {
return err
}
}
return err
}
+151
View File
@@ -0,0 +1,151 @@
package service
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"mbase/app/handler"
"mbase/app/logic"
"mbase/pkg/logger"
"mbase/pkg/netacl"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
)
type ServiceConfig struct {
Handler *handler.Handler
Logic *logic.Logic
NetACL *netacl.NetACL
Portnum uint32
Address string
Protocol string
Hostname string
X509Cert []byte
X509Key []byte
}
type Service struct {
gsrv *grpc.Server
hand *handler.Handler
lg *logic.Logic
log *logger.Logger
nacl *netacl.NetACL
portnum uint32
address string
protocol string
hostname string
username string
password string
x509Cert []byte
x509Key []byte
}
func NewService(conf *ServiceConfig) *Service {
svc := Service{
hand: conf.Handler,
lg: conf.Logic,
nacl: conf.NetACL,
portnum: conf.Portnum,
address: conf.Address,
protocol: conf.Protocol,
hostname: conf.Hostname,
x509Cert: conf.X509Cert,
x509Key: conf.X509Key,
}
svc.log = logger.NewLogger("gservice")
return &svc
}
func (svc *Service) Run() error {
var err error
svc.log.Infof("Service run")
listenSpec := fmt.Sprintf("%s:%d", svc.address, svc.portnum)
listener, err := net.Listen(svc.protocol, listenSpec)
if err != nil {
return err
}
tlsCert, err := tls.X509KeyPair(svc.x509Cert, svc.x509Key)
if err != nil {
return err
}
tlsConfig := tls.Config{
Certificates: []tls.Certificate{tlsCert},
ClientAuth: tls.NoClientCert,
InsecureSkipVerify: true,
}
tlsCredentials := credentials.NewTLS(&tlsConfig)
if err != nil {
return err
}
interceptors := []grpc.UnaryServerInterceptor{
svc.accessInterceptor,
svc.logInterceptor,
}
gsrvOpts := []grpc.ServerOption{
grpc.Creds(tlsCredentials),
grpc.ChainUnaryInterceptor(interceptors...),
}
svc.gsrv = grpc.NewServer(gsrvOpts...)
svc.hand.Register(svc.gsrv)
svc.log.Infof("Service listening at %v", listener.Addr())
err = svc.gsrv.Serve(listener)
if err != nil {
return err
}
return err
}
func (svc *Service) accessInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
peerMeta, _ := peer.FromContext(ctx)
host, _, err := net.SplitHostPort(peerMeta.Addr.String())
if err != nil {
return nil, err
}
addressEnabled, _ := svc.nacl.AddressIsEnabled(host)
if err != nil {
return nil, err
}
if addressEnabled {
svc.log.Warningf("Enable access from %s", host)
return handler(ctx, req)
}
svc.log.Warningf("Disable access from %s", host)
return nil, fmt.Errorf("Access disabled by network ACL")
}
func (svc *Service) logInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
meta, _ := metadata.FromIncomingContext(ctx)
peerMeta, _ := peer.FromContext(ctx)
svc.log.Infof("User %v called %v from %s", meta["username"], info.FullMethod, peerMeta.Addr.String())
return handler(ctx, req)
}
func (svc *Service) debugInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
var err error
reqBinary, err := json.Marshal(req)
requestString := ""
if err == nil {
requestString = string(reqBinary)
}
svc.log.Debugf("Called method: %v with params %v", info.FullMethod, requestString)
return handler(ctx, req)
}
func (svc *Service) Stop() {
svc.log.Infof("Stopping service")
svc.gsrv.GracefulStop()
}
+63
View File
@@ -0,0 +1,63 @@
package main
import (
"context"
"mbase/pkg/mbctl"
)
func (util *Util) CreateAccount(ctx context.Context, operID int64) (*mbctl.CreateAccountResult, error) {
var err error
res := &mbctl.CreateAccountResult{}
params := &mbctl.CreateAccountParams{
Username: util.username,
Password: util.password,
}
res, err = util.lg.CreateAccount(ctx, operID, params)
if err != nil {
return res, err
}
return res, err
}
func (util *Util) DeleteAccount(ctx context.Context, operID int64) (*mbctl.DeleteAccountResult, error) {
var err error
res := &mbctl.DeleteAccountResult{}
params := &mbctl.DeleteAccountParams{
Username: util.username,
AccountID: util.accountID,
}
res, err = util.lg.DeleteAccount(ctx, operID, params)
if err != nil {
return res, err
}
return res, err
}
func (util *Util) ListAccounts(ctx context.Context, operID int64) (*mbctl.ListAccountsResult, error) {
var err error
res := &mbctl.ListAccountsResult{}
params := &mbctl.ListAccountsParams{}
res, err = util.lg.ListAccounts(ctx, operID, params)
if err != nil {
return res, err
}
return res, err
}
func (util *Util) UpdateAccount(ctx context.Context, operID int64) (*mbctl.UpdateAccountResult, error) {
var err error
res := &mbctl.UpdateAccountResult{}
params := &mbctl.UpdateAccountParams{
Username: util.username,
AccountID: util.accountID,
NewUsername: util.newUsername,
NewPassword: util.newPassword,
}
res, err = util.lg.UpdateAccount(ctx, operID, params)
if err != nil {
return res, err
}
return res, err
}
+40
View File
@@ -0,0 +1,40 @@
package main
import (
"context"
"mbase/pkg/mbctl"
)
func (util *Util) SetGrant(ctx context.Context, operID int64) (*mbctl.SetGrantResult, error) {
var err error
res := &mbctl.SetGrantResult{}
params := &mbctl.SetGrantParams{
Username: util.username,
AccountID: util.accountID,
Operation: util.operation,
}
res, err = util.lg.SetGrant(ctx, operID, params)
if err != nil {
return res, err
}
if err != nil {
return res, err
}
return res, err
}
func (util *Util) DeleteGrant(ctx context.Context, operID int64) (*mbctl.DeleteGrantResult, error) {
var err error
res := &mbctl.DeleteGrantResult{}
params := &mbctl.DeleteGrantParams{
Username: util.username,
AccountID: util.accountID,
Operation: util.operation,
}
res, err = util.lg.DeleteGrant(ctx, operID, params)
if err != nil {
return res, err
}
return res, err
}
+467
View File
@@ -0,0 +1,467 @@
/*
* Copyright 2024 Oleg Borodin <borodin@unix7.org>
*/
package main
import (
"context"
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"mbase/app/config"
"mbase/app/database"
"mbase/app/descriptor"
"mbase/app/logic"
"sigs.k8s.io/yaml"
)
const (
rcFilename = ".certmanager.yaml"
helpCmd = "help"
createAccountCmd = "createAccount"
updateAccountCmd = "updateAccount"
deleteAccountCmd = "deleteAccount"
listAccountsCmd = "listAccounts"
setGrantCmd = "setGrant"
deleteGrantCmd = "deleteGrant"
initDatabaseCmd = "initDatabase"
seedAccountCmd = "seedAccount"
)
func main() {
var err error
util := NewUtil()
err = util.Build()
if err != nil {
fmt.Printf("Build error: %v\n", err)
os.Exit(1)
}
err = util.Exec()
if err != nil {
fmt.Printf("Exec error: %v\n", err)
os.Exit(1)
}
os.Exit(0)
}
type Util struct {
subCmd string
cmdTimeout int64
conf *config.Config
lg *logic.Logic
db *database.Database
state descriptor.Server
sfile string
accessUsername string
accessPassword string
accountID int64
username string
password string
disable bool
newUsername string
newPassword string
operation string
quiet bool
}
func NewUtil() *Util {
var util Util
util.cmdTimeout = 120
return &util
}
func (util *Util) GetOpt() error {
var err error
exeName := filepath.Base(os.Args[0])
flag.Int64Var(&util.cmdTimeout, "timeout", util.cmdTimeout, "command execution timeout")
flag.StringVar(&util.accessUsername, "user", util.accessUsername, "access login")
flag.StringVar(&util.accessPassword, "pass", util.accessPassword, "access password")
flag.BoolVar(&util.quiet, "quiet", util.quiet, "don't print result")
help := func() {
fmt.Println("")
fmt.Printf("Usage: %s [option] command [command option]\n", exeName)
fmt.Printf("\n")
fmt.Printf(" %s, %s, %s, %s,\n",
createAccountCmd,
deleteAccountCmd,
listAccountsCmd,
updateAccountCmd)
fmt.Printf(" %s, %s\n",
setGrantCmd,
deleteGrantCmd)
fmt.Printf(" %s, %s\n",
initDatabaseCmd,
seedAccountCmd)
fmt.Printf("\n")
fmt.Printf("Global options:\n")
flag.PrintDefaults()
fmt.Printf("\n")
}
flag.Usage = help
flag.Parse()
args := flag.Args()
var subCmd string
var subArgs []string
if len(args) > 0 {
subCmd = args[0]
subArgs = args[1:]
}
util.subCmd = subCmd
switch subCmd {
case helpCmd:
help()
return errors.New("Unknown command")
case createAccountCmd:
flagSet := flag.NewFlagSet(createAccountCmd, flag.ExitOnError)
flagSet.StringVar(&util.username, "username", util.username, "user name")
flagSet.StringVar(&util.password, "password", util.password, "user password")
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
case deleteAccountCmd:
flagSet := flag.NewFlagSet(deleteAccountCmd, flag.ExitOnError)
flagSet.StringVar(&util.username, "username", util.username, "user name")
flagSet.Int64Var(&util.accountID, "accountId", util.accountID, "account ID")
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
case listAccountsCmd:
flagSet := flag.NewFlagSet(listAccountsCmd, flag.ExitOnError)
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
case updateAccountCmd:
flagSet := flag.NewFlagSet(updateAccountCmd, flag.ExitOnError)
flagSet.StringVar(&util.username, "username", util.username, "user name")
flagSet.Int64Var(&util.accountID, "accountId", util.accountID, "account ID")
flagSet.StringVar(&util.newUsername, "newUsername", util.newUsername, "new user name")
flagSet.StringVar(&util.newPassword, "newPassword", util.newPassword, "new user password")
flagSet.BoolVar(&util.disable, "disable", util.disable, "disable account")
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
case setGrantCmd:
flagSet := flag.NewFlagSet(setGrantCmd, flag.ExitOnError)
flagSet.StringVar(&util.username, "username", util.username, "user name")
flagSet.Int64Var(&util.accountID, "accountId", util.accountID, "account ID")
flagSet.StringVar(&util.operation, "operation", util.operation, "grant type")
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
case deleteGrantCmd:
flagSet := flag.NewFlagSet(deleteGrantCmd, flag.ExitOnError)
flagSet.StringVar(&util.username, "username", util.username, "user name")
flagSet.Int64Var(&util.accountID, "accountId", util.accountID, "account ID")
flagSet.StringVar(&util.operation, "operation", util.operation, "grant type")
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
case initDatabaseCmd:
flagSet := flag.NewFlagSet(initDatabaseCmd, flag.ExitOnError)
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
case seedAccountCmd:
flagSet := flag.NewFlagSet(seedAccountCmd, flag.ExitOnError)
flagSet.Usage = func() {
fmt.Printf("\n")
fmt.Printf("Usage: %s [global options] %s [command options]\n", exeName, subCmd)
fmt.Printf("\n")
fmt.Printf("The command options: none\n")
flagSet.PrintDefaults()
fmt.Printf("\n")
}
flagSet.Parse(subArgs)
util.subCmd = subCmd
default:
help()
return errors.New("Unknown command")
}
return err
}
func (util *Util) Build() error {
var err error
util.conf = config.NewConfig()
err = util.conf.ReadFile()
if err != nil {
return err
}
err = util.conf.ReadEnv()
if err != nil {
return err
}
util.sfile = filepath.Join(util.conf.DataDir, "certmanager.yaml")
db, err := database.NewDatabase(util.conf.DataDir)
if err != nil {
return err
}
err = db.OpenDatabase()
if err != nil {
return err
}
util.db = db
logicConfig := &logic.LogicConfig{
Database: util.db,
}
util.lg, err = logic.NewLogic(logicConfig)
if err != nil {
return err
}
return err
}
type Response struct {
Command string `json:"command" yaml:"command"`
Args []string `json:"args" yaml:"args"`
Error bool `json:"error" yaml:"error"`
Message string `json:"message,omitempty" yaml:"message,omitempty"`
Result any `json:"result,omitempty" yaml:"result,omitempty"`
}
func (util *Util) Exec() error {
var err error
err = util.GetOpt()
if err != nil {
return err
}
var timeout = time.Duration(util.cmdTimeout) * time.Second
ctx, close := context.WithTimeout(context.Background(), timeout)
defer close()
var res any
switch util.subCmd {
case initDatabaseCmd:
res, err = util.InitDatabase(ctx)
case seedAccountCmd:
res, err = util.SeedAccount(ctx)
default:
authOk, operID, localErr := util.lg.ValidateAcount(ctx, util.accessUsername, util.accessPassword)
if err != nil {
err = localErr
goto exit
}
if !authOk {
err = fmt.Errorf("Incorrect username or password")
goto exit
}
switch util.subCmd {
case createAccountCmd:
res, err = util.CreateAccount(ctx, operID)
case updateAccountCmd:
res, err = util.UpdateAccount(ctx, operID)
case listAccountsCmd:
res, err = util.ListAccounts(ctx, operID)
case deleteAccountCmd:
res, err = util.DeleteAccount(ctx, operID)
case setGrantCmd:
res, err = util.SetGrant(ctx, operID)
case deleteGrantCmd:
res, err = util.DeleteGrant(ctx, operID)
default:
err = errors.New("Unknown cli command")
}
}
exit:
var resp Response
resp.Command = util.subCmd
resp.Args = os.Args
if err != nil {
resp.Error = true
resp.Message = fmt.Sprintf("%v", err)
} else {
resp.Result = res
}
if !resp.Error && !util.quiet {
respBytes, _ := yaml.Marshal(resp)
fmt.Println(string(respBytes))
}
return err
}
type InitDatabaseRes struct{}
func (util *Util) InitDatabase(ctx context.Context) (InitDatabaseRes, error) {
res := InitDatabaseRes{}
// Initialize database
// Load state
err := util.LoadState()
if err != nil {
return res, err
}
if !util.state.DatabaseInitialized {
if util.db == nil {
err = fmt.Errorf("Nil db object")
return res, err
}
err := util.db.InitDatabase()
if err != nil {
return res, err
}
util.state.DatabaseInitialized = true
err = util.SaveState()
if err != nil {
return res, err
}
}
return res, err
}
type SeedAccountRes struct{}
func (util *Util) SeedAccount(ctx context.Context) (SeedAccountRes, error) {
// Seed accounts
res := SeedAccountRes{}
_, err := util.lg.SeedAccount(ctx)
if err != nil {
return res, err
}
return res, err
}
func (util *Util) LoadState() error {
var err error
_, err = os.Stat(util.sfile)
if os.IsNotExist(err) {
err = nil
return err
}
file, err := os.Open(util.sfile)
if err != nil {
return err
}
defer file.Close()
stateBytes, err := ioutil.ReadAll(file)
if err != nil {
return err
}
err = yaml.Unmarshal(stateBytes, &util.state)
if err != nil {
return err
}
return err
}
func (util *Util) SaveState() error {
var err error
file, err := os.OpenFile(util.sfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
util.state.UpdatedAt = time.Now().Format(time.RFC3339)
if util.state.CreatedAt == "" {
util.state.CreatedAt = util.state.UpdatedAt
}
stateBytes, err := yaml.Marshal(util.state)
if err != nil {
return err
}
_, err = file.Write(stateBytes)
if err != nil {
return err
}
return err
}
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package main
import (
"os"
)
func main() {
var err error
util := NewUtil()
err = util.Build()
if err != nil {
os.Exit(1)
}
err = util.Exec(os.Args[1:])
if err != nil {
os.Exit(1)
}
}
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package main
import (
"fmt"
"sigs.k8s.io/yaml"
)
func printResponse(res any, err error) {
type Response struct {
Error bool `json:"error" yaml:"error"`
Message string `json:"message,omitempty" yaml:"message,omitempty"`
Result any `json:"result,omitempty" yaml:"result,omitempty"`
}
resp := Response{}
if err != nil {
resp.Error = true
resp.Message = err.Error()
} else {
resp.Result = res
}
respBytes, _ := yaml.Marshal(resp)
fmt.Printf("---\n%s\n", string(respBytes))
}
+220
View File
@@ -0,0 +1,220 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package main
import (
"os"
"path/filepath"
"context"
"io"
"bytes"
"mbase/app/config"
"mbase/app/database"
"mbase/app/descriptor"
"mbase/pkg/auxtool"
"mbase/pkg/logger"
"go.yaml.in/yaml/v4"
"github.com/spf13/cobra"
)
type Util struct {
rootCmd *cobra.Command
dumpDatabaseParams dumpDatabaseParams
restoreDatabaseParams restoreDatabaseParams
}
func NewUtil() *Util {
return &Util{}
}
func (util *Util) GetRooCmd() *cobra.Command {
return util.rootCmd
}
func (util *Util) Build() error {
var err error
execName := filepath.Base(os.Args[0])
rootCmd := &cobra.Command{
Use: execName,
Short: "\nDump application database",
SilenceUsage: true,
}
rootCmd.CompletionOptions.DisableDefaultCmd = true
var dumpDatabaseCmd = &cobra.Command{
Use: "dump [filename|-]",
Short: "Dump application database",
Args: cobra.ExactArgs(1),
Run: util.DumpDatabase,
}
rootCmd.AddCommand(dumpDatabaseCmd)
var restoreDatabaseCmd = &cobra.Command{
Use: "restore [] [filename|-]",
Short: "Restore application database",
Args: cobra.ExactArgs(1),
Run: util.RestoreDatabase,
}
restoreDatabaseCmd.Flags().BoolVarP(&util.restoreDatabaseParams.DeleteAllRecords, "clean", "C", false, "Clean all record")
rootCmd.AddCommand(restoreDatabaseCmd)
util.rootCmd = rootCmd
return err
}
func (util *Util) Exec(args []string) error {
var err error
util.rootCmd.SetArgs(args)
err = util.rootCmd.Execute()
return err
}
func (util *Util) DumpDatabase(cmd *cobra.Command, args []string) {
util.dumpDatabaseParams.Filename = args[0]
res, err := util.dumpDatabase(util.dumpDatabaseParams)
printResponse(res, err)
}
type dumpDatabaseParams struct {
Filename string
}
type dumpDatabaseResult struct {}
func (util *Util) dumpDatabase(params dumpDatabaseParams) (dumpDatabaseResult, error) {
var err error
res := dumpDatabaseResult{}
ctx := context.Background()
conf := config.NewConfig()
err = conf.ReadFile()
if err != nil {
return res, err
}
err = conf.ReadEnv()
if err != nil {
return res, err
}
db, err := database.NewDatabase(conf.DataDir)
if err != nil {
return res, err
}
err = db.OpenDatabase()
if err != nil {
return res, err
}
file := os.Stdout
if params.Filename != "-" {
file, err = os.OpenFile(params.Filename, os.O_CREATE|os.O_WRONLY, 0640)
if err != nil {
return res, err
}
defer file.Close()
}
listAccounts, err := db.CompletedListAccounts(ctx)
if err != nil {
return res, err
}
listGrants, err := db.ListGrants(ctx)
if err != nil {
return res, err
}
dump := descriptor.Dump{
Timestamp: auxtool.TimeNow(),
Accounts: listAccounts,
Grants: listGrants,
}
dumpBytes, err := yaml.Marshal(dump)
if err != nil {
return res, err
}
_, err = file.Write(dumpBytes)
if err != nil {
return res, err
}
return res, err
}
func (util *Util) RestoreDatabase(cmd *cobra.Command, args []string) {
util.restoreDatabaseParams.Filename = args[0]
res, err := util.restoreDatabase(util.restoreDatabaseParams)
printResponse(res, err)
}
type restoreDatabaseParams struct {
Filename string
DeleteAllRecords bool
}
type restoreDatabaseResult struct {}
func (util *Util) restoreDatabase(params restoreDatabaseParams) (restoreDatabaseResult, error) {
var err error
res := restoreDatabaseResult{}
ctx := context.Background()
log := logger.NewLogger("restore")
conf := config.NewConfig()
err = conf.ReadFile()
if err != nil {
return res, err
}
err = conf.ReadEnv()
if err != nil {
return res, err
}
db, err := database.NewDatabase(conf.DataDir)
if err != nil {
return res, err
}
err = db.OpenDatabase()
if err != nil {
return res, err
}
file := os.Stdin
if params.Filename != "-" {
file, err = os.Open(params.Filename)
if err != nil {
return res, err
}
defer file.Close()
}
buffer := bytes.NewBuffer(nil)
_, err = io.Copy(buffer, file)
if err != nil {
return res, err
}
dump := descriptor.Dump{}
err = yaml.Unmarshal(buffer.Bytes(), &dump)
if err != nil {
return res, err
}
if params.DeleteAllRecords {
err = db.CleanDatabase(ctx)
if err != nil {
return res, err
}
}
for _, account := range dump.Accounts {
log.Infof("Insert account %s", account.Username)
err = db.InsertAccount(ctx, &account)
if err != nil {
log.Errorf("Insert account error: %v", err)
}
}
for _, grant := range dump.Grants {
log.Infof("Insert grant %s for account %d", grant.Operation, grant.AccountID)
err = db.InsertGrant(ctx, &grant)
if err != nil {
log.Errorf("Insert account error: %v", err)
}
}
return res, err
}
+169
View File
@@ -0,0 +1,169 @@
/*
* Copyright 2022 Oleg Borodin <borodin@unix7.org>
*/
package main
import (
"bytes"
"context"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"time"
"mbase/app/config"
"mbase/app/database"
"mbase/app/descriptor"
"mbase/pkg/logger"
"go.yaml.in/yaml/v4"
)
func main() {
var err error
util := NewUtil()
err = util.Exec()
if err != nil {
fmt.Printf("Exec error: %s\n", err)
}
}
type Util struct {
conf *config.Config
db *database.Database
log *logger.Logger
filename string
deleteAllRecords bool
}
func NewUtil() *Util {
var util Util
util.log = logger.NewLogger("logic")
return &util
}
func (util *Util) GetOpt() error {
var err error
exeName := filepath.Base(os.Args[0])
help := func() {
fmt.Println("")
fmt.Printf("Usage: %s [option]\n", exeName)
fmt.Printf("\n")
flag.PrintDefaults()
fmt.Printf("\n")
}
flag.Usage = help
flag.StringVar(&util.filename, "file", util.filename, "dump file name")
flag.BoolVar(&util.deleteAllRecords, "deleteAllRecords", util.deleteAllRecords, "delete all existing records before restoring")
flag.Parse()
return err
}
func (util *Util) Exec() error {
var err error
err = util.GetOpt()
if err != nil {
return err
}
const timeout = 30 * time.Second
ctx, _ := context.WithTimeout(context.Background(), timeout)
err = util.RestoreRecords(ctx)
type ErrorDescr struct {
Error bool `json:"error,omitempty"`
Message string `json:"errorMessage,omitempty" yaml:"errorMessage,omitempty"`
}
errDescr := ErrorDescr{}
if err != nil {
errDescr.Error = true
errDescr.Message = fmt.Sprintf("%v", err)
}
errBytes, _ := yaml.Marshal(errDescr)
fmt.Printf("%s\n", string(errBytes))
return err
}
func (util *Util) RestoreRecords(ctx context.Context) error {
var err error
util.conf = config.NewConfig()
err = util.conf.ReadFile()
if err != nil {
return err
}
err = util.conf.ReadEnv()
if err != nil {
return err
}
db, err := database.NewDatabase(util.conf.DataDir)
if err != nil {
return err
}
util.db = db
err = util.db.OpenDatabase()
if err != nil {
return err
}
file := os.Stdin
if util.filename != "" {
file, err = os.Open(util.filename)
if err != nil {
return err
}
defer file.Close()
}
buffer := bytes.NewBuffer(nil)
_, err = io.Copy(buffer, file)
if err != nil {
return err
}
dump := descriptor.Dump{}
err = yaml.Unmarshal(buffer.Bytes(), &dump)
if err != nil {
return err
}
if util.deleteAllRecords {
err = util.db.CleanDatabase(ctx)
if err != nil {
return err
}
}
for _, account := range dump.Accounts {
util.log.Infof("Insert account %s", account.Username)
err = util.db.InsertAccount(ctx, &account)
if err != nil {
util.log.Errorf("Insert account error: %v", err)
}
}
for _, grant := range dump.Grants {
util.log.Infof("Insert grant %s for account %d", grant.Operation, grant.AccountID)
err = util.db.InsertGrant(ctx, &grant)
if err != nil {
util.log.Errorf("Insert account error: %v", err)
}
}
return err
}
Executable
+351
View File
@@ -0,0 +1,351 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2024-06-19.01; # UTC
# Copyright (C) 1999-2024 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN* | MSYS*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/* | msys/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.lo | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
GNU Automake home page: <https://www.gnu.org/software/automake/>.
General help using GNU software: <https://www.gnu.org/gethelp/>.
EOF
exit $?
;;
-v | --v*)
echo "compile (GNU Automake) $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
clang-cl | *[/\\]clang-cl | clang-cl.exe | *[/\\]clang-cl.exe | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
Vendored Executable
+1815
View File
File diff suppressed because it is too large Load Diff
Vendored Executable
+2354
View File
File diff suppressed because it is too large Load Diff
Vendored Executable
+6918
View File
File diff suppressed because it is too large Load Diff
+247
View File
@@ -0,0 +1,247 @@
AC_INIT([mbase],[0.0.1])
AM_INIT_AUTOMAKE([foreign subdir-objects tar-pax])
AC_PREFIX_DEFAULT(/usr/local)
PACKAGE=mbase
AC_CHECK_PROG(HAVE_GO, go, true, false, [$PATH:/usr/local/bin])
if test "x$HAVE_GO" = "xfalse"; then
AC_MSG_ERROR([Requested program go not found])
fi
AC_PATH_PROG([GO],[go],, [$PATH:/usr/local/bin])
AC_PATH_PROGS([CP],[gcp cp])
if test -z "$CP"; then
AC_MSG_ERROR([Requested program cp not found])
fi
AC_PATH_PROGS([DBUILDPACKAGE],[dpkg-buildpackage true])
AC_PATH_PROGS([RPMBUILD],[rpmbuild true])
AC_PATH_PROGS([PROTOC],[protoc true])
AC_PATH_PROGS([PODMAN],[podman true])
AC_PATH_PROGS([CPIO],[cpio false])
AC_PATH_PROGS([XARGS],[xargs false])
AC_PATH_PROGS([PROTOC],[protoc false])
AC_PROG_INSTALL
AC_PROG_MKDIR_P
AC_CANONICAL_HOST
AC_PROG_CC
dnl --------------------------------------------------------------------------------------
case $host_os in
*freebsd* )
AC_SUBST(ROOT_GROUP, "wheel")
AM_CONDITIONAL(FREEBSD_OS, true)
AM_CONDITIONAL(LINUX_OS, false)
OSNAME=freebsd
ROOT_GROUP=wheel
;;
*linux* )
AC_SUBST(ROOT_GROUP, "root")
AM_CONDITIONAL(FREEBSD_OS, false)
AM_CONDITIONAL(LINUX_OS, true)
OSNAME=linux
ROOT_GROUP=root
;;
esac
AM_CONDITIONAL(SYSTEMD, false)
if test -d /lib/systemd/system; then
AM_CONDITIONAL(SYSTEMD, true)
fi
test "x$prefix" == "xNONE" && prefix=$ac_default_prefix
test "x$libexecdir" == "xNONE" && libexecdir=${prefix}/lib
AC_ARG_ENABLE([devel-mode],
AS_HELP_STRING([--enable-devel-mode], [Enable developmend mode]))
AC_DEFINE_UNQUOTED(srv_devel_mode, "false", [developmend mode])
AC_SUBST(srv_devel_mode, "false")
AS_IF([test "x$enable_devel_mode" = "xyes"], [
AC_DEFINE_UNQUOTED(srv_devel_mode, "true", [developmend mode])
AC_SUBST(srv_devel_mode, "true")
SRCDIR=`pwd`
enable_devel_mode=yes
])
dnl --------------------------------------------------------------------------------------
SRV_CONFDIR=${prefix}/etc/${PACKAGE}
AC_ARG_WITH(confdir,
AS_HELP_STRING([--with-confdir=PATH],[set configuration dir to PATH (default: SRV_CONFDIR")]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_CONFDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-confdir=PATH)
;;
esac
fi ])
if test "x$prefix" == "x/usr"; then
SRV_CONFDIR=/etc/${PACKAGE}
fi
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_CONFDIR="${SRCDIR}/etc/${PACKAGE}"
])
AC_DEFINE_UNQUOTED(SRV_CONFDIR, "$SRV_CONFDIR", [location of configuration files for ${PACKAGE}])
AC_SUBST(srv_confdir, "$SRV_CONFDIR")
AC_SUBST(SRV_CONFDIR, "$SRV_CONFDIR")
AC_MSG_NOTICE(srv_confdir set as ${SRV_CONFDIR})
dnl --------------------------------------------------------------------------------------
SRV_LOGDIR="/var/log/${PACKAGE}"
AC_ARG_WITH(logdir,
AS_HELP_STRING([--with-logdir=PATH],[set file path for source logdir (default: $SRV_LOGDIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_LOGDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-logdir=PATH)
;;
esac
fi ])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_LOGDIR="${SRCDIR}/tmp/log"
])
AC_DEFINE_UNQUOTED(SRV_LOGDIR, "$SRV_LOGDIR", [location of logdir])
AC_SUBST(srv_logdir, "$SRV_LOGDIR")
AC_SUBST(SRV_LOGDIR, "$SRV_LOGDIR")
AC_MSG_NOTICE(srv_logdir set as ${SRV_LOGDIR})
dnl --------------------------------------------------------------------------------------
SRV_RUNDIR="/var/run/${PACKAGE}"
AC_ARG_WITH(rundir,
AS_HELP_STRING([--with-rundir=PATH],[set file path for source rundir (default: $SRV_RUNDIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_RUNDIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-rundir=PATH)
;;
esac
fi ])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_RUNDIR="${SRCDIR}/tmp/run"
])
AC_DEFINE_UNQUOTED(SRV_RUNDIR, "$SRV_RUNDIR", [location of rundir])
AC_SUBST(srv_rundir, "$SRV_RUNDIR")
AC_SUBST(SRV_RUNDIR, "$SRV_RUNDIR")
AC_MSG_NOTICE(srv_rundir set as ${SRV_RUNDIR})
dnl --------------------------------------------------------------------------------------
SRV_DATADIR="/var/data/${PACKAGE}"
AC_ARG_WITH(datadir,
AS_HELP_STRING([--with-datadir=PATH],[set file path for source datadir (default: $SRV_DATADIR)]),
[ if test ! -z "$withval" ; then
case $withval in
/*)
SRV_DATADIR="$withval"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-datadir=PATH)
;;
esac
fi ])
AS_IF([test "x$enable_devel_mode" = "xyes"], [
SRV_DATADIR="${SRCDIR}/tmp/data"
])
AC_DEFINE_UNQUOTED(SRV_DATADIR, "$SRV_DATADIR", [location of datadir])
AC_SUBST(srv_datadir, "$SRV_DATADIR")
AC_SUBST(SRV_DATADIR, "$SRV_DATADIR")
AC_MSG_NOTICE(srv_datadir set as ${SRV_DATADIR})
dnl --------------------------------------------------------------------------------------
AM_CONDITIONAL(MAKEALL, true)
AM_CONDITIONAL(MAKESERVICE, true)
AM_CONDITIONAL(MAKECONTROL, true)
AM_CONDITIONAL(MAKECONTAINER, false)
AS_IF([test "x$enable_devel_mode" = "xyes"], [
AM_CONDITIONAL(DEV_MODE, true)
],[
AM_CONDITIONAL(DEV_MODE, false)
]
)
AC_ARG_ENABLE([only-service], AS_HELP_STRING([--enable-only-service], [Compile only service part]),
[
AM_CONDITIONAL(MAKEALL, false)
AM_CONDITIONAL(MAKECONTROL, false)
]
)
AC_ARG_ENABLE([only-control], AS_HELP_STRING([--enable-only-control], [Compile only control part]),
[
AM_CONDITIONAL(MAKEALL, false)
AM_CONDITIONAL(MAKECONTROL, true)
AM_CONDITIONAL(MAKESERVICE, false)
]
)
AC_ARG_ENABLE([container-mode], AS_HELP_STRING([--enable-container-mode], [Compile only service part wo tools]),
[
AM_CONDITIONAL(MAKEALL, false)
AM_CONDITIONAL(MAKESERVICE, true)
AM_CONDITIONAL(MAKECONTAINER, true)
AM_CONDITIONAL(MAKECONTROL, false)
]
)
AS_IF([test "x$enable_only_service" = "xyes" && test "x$enable_only_control" = "xyes"],
AM_CONDITIONAL(MAKEALL, true)
AM_CONDITIONAL(MAKESERVICE, true)
)
dnl AS_IF([test "x$enable_only_control" = "xyes"],
dnl AM_CONDITIONAL(INSTALLRC, false)
dnl )
AC_SUBST(srv_name, "$PACKAGE")
AC_SUBST(srv_sbindir, "${prefix}/sbin")
AC_CONFIG_FILES([
Makefile
app/config/variant.go
initrc/mbased.service
initrc/mbased
])
AC_OUTPUT
+1
View File
@@ -0,0 +1 @@
daemon: false
+54
View File
@@ -0,0 +1,54 @@
module mbase
go 1.26.2
require (
github.com/gin-gonic/gin v1.12.0
github.com/jmoiron/sqlx v1.4.0
github.com/mattn/go-sqlite3 v1.14.44
github.com/sirupsen/logrus v1.9.4
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
go.yaml.in/yaml/v4 v4.0.0-rc.4
google.golang.org/grpc v1.81.1
google.golang.org/protobuf v1.36.11
sigs.k8s.io/yaml v1.6.0
)
require (
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/arch v0.22.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+154
View File
@@ -0,0 +1,154 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8=
github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
+22
View File
@@ -0,0 +1,22 @@
#!/bin/sh
#
# PROVIDE: mbased
# REQUIRE: DAEMON
. /etc/rc.subr
name="mbased"
rcvar="mbased_enable"
pidfile="@srv_rundir@"/mbased.pid
command="@prefix@/sbin/${name}"
command_args="-daemon"
procname="@prefix@/sbin/${name}"
load_rc_config ${name}
: ${mbased_enable:="NO"}
run_rc_command "$1"
#EOF
+13
View File
@@ -0,0 +1,13 @@
[Unit]
Description=mbased
[Service]
Type=forking
ExecStart=@srv_sbindir@/mbased
ExecReload=/bin/kill -HUP $MAINPID
ExecRestart=/bin/kill -HUP $MAINPID
ExecStartPre=/usr/bin/install -d -o root -g root @srv_rundir@ @srv_logdir@ @srv_datadir@
[Install]
WantedBy=multi-user.target
Executable
+541
View File
@@ -0,0 +1,541 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2024-06-19.01; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
# Create dirs (including intermediate dirs) using mode 755.
# This is like GNU 'install' as of coreutils 8.32 (2020).
mkdir_umask=22
backupsuffix=
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-p pass -p to $cpprog.
-s $stripprog installed files.
-S SUFFIX attempt to back up existing files, with suffix SUFFIX.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
By default, rm is invoked with -f; when overridden with RMPROG,
it's up to you to specify -f if you want it.
If -S is not specified, no backups are attempted.
Report bugs to <bug-automake@gnu.org>.
GNU Automake home page: <https://www.gnu.org/software/automake/>.
General help using GNU software: <https://www.gnu.org/gethelp/>."
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-p) cpprog="$cpprog -p";;
-s) stripcmd=$stripprog;;
-S) backupsuffix="$2"
shift;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 (GNU Automake) $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
# Don't chown directories that already exist.
if test $dstdir_status = 0; then
chowncmd=""
fi
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
# The $RANDOM variable is not portable (e.g., dash). Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap '
ret=$?
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
exit $ret
' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p'.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibility with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask &&
{ test -z "$stripcmd" || {
# Create $dsttmp read-write so that cp doesn't create it read-only,
# which would cause strip to fail.
if test -z "$doit"; then
: >"$dsttmp" # No need to fork-exec 'touch'.
else
$doit touch "$dsttmp"
fi
}
} &&
$doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# If $backupsuffix is set, and the file being installed
# already exists, attempt a backup. Don't worry if it fails,
# e.g., if mv doesn't support -f.
if test -n "$backupsuffix" && test -f "$dst"; then
$doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
fi
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
Executable
+236
View File
@@ -0,0 +1,236 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU and other programs.
scriptversion=2024-06-07.14; # UTC
# shellcheck disable=SC2006,SC2268 # we must support pre-POSIX shells
# Copyright (C) 1996-2024 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try '$0 --help' for more information"
exit 1
fi
case $1 in
--is-lightweight)
# Used by our autoconf macros to check whether the available missing
# script is modern enough.
exit 0
;;
--run)
# Back-compat with the calling convention used by older automake.
shift
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
to PROGRAM being missing or too old.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal autoconf autogen autoheader autom4te automake autoreconf
bison flex help2man lex makeinfo perl yacc
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
'g' are ignored when checking the name.
Report bugs to <bug-automake@gnu.org>.
GNU Automake home page: <https://www.gnu.org/software/automake/>.
General help using GNU software: <https://www.gnu.org/gethelp/>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing (GNU Automake) $scriptversion"
exit $?
;;
-*)
echo 1>&2 "$0: unknown '$1' option"
echo 1>&2 "Try '$0 --help' for more information"
exit 1
;;
esac
# Run the given program, remember its exit status.
"$@"; st=$?
# If it succeeded, we are done.
test $st -eq 0 && exit 0
# Also exit now if we it failed (or wasn't found), and '--version' was
# passed; such an option is passed most likely to detect whether the
# program is present and works.
case $2 in --version|--help) exit $st;; esac
# Exit code 63 means version mismatch. This often happens when the user
# tries to use an ancient version of a tool on a file that requires a
# minimum version.
if test $st -eq 63; then
msg="probably too old"
elif test $st -eq 127; then
# Program was missing.
msg="missing on your system"
else
# Program was found and executed, but failed. Give up.
exit $st
fi
perl_URL=https://www.perl.org/
flex_URL=https://github.com/westes/flex
gnu_software_URL=https://www.gnu.org/software
program_details ()
{
case $1 in
aclocal|automake|autoreconf)
echo "The '$1' program is part of the GNU Automake package:"
echo "<$gnu_software_URL/automake>"
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/autoconf>"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
autoconf|autom4te|autoheader)
echo "The '$1' program is part of the GNU Autoconf package:"
echo "<$gnu_software_URL/autoconf/>"
echo "It also requires GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
*)
:
;;
esac
}
give_advice ()
{
# Normalize program name to check for.
normalized_program=`echo "$1" | sed '
s/^gnu-//; t
s/^gnu//; t
s/^g//; t'`
printf '%s\n' "'$1' is $msg."
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
autoheader_deps="'acconfig.h'"
automake_deps="'Makefile.am'"
aclocal_deps="'acinclude.m4'"
case $normalized_program in
aclocal*)
echo "You should only need it if you modified $aclocal_deps or"
echo "$configure_deps."
;;
autoconf*)
echo "You should only need it if you modified $configure_deps."
;;
autogen*)
echo "You should only need it if you modified a '.def' or '.tpl' file."
echo "You may want to install the GNU AutoGen package:"
echo "<$gnu_software_URL/autogen/>"
;;
autoheader*)
echo "You should only need it if you modified $autoheader_deps or"
echo "$configure_deps."
;;
automake*)
echo "You should only need it if you modified $automake_deps or"
echo "$configure_deps."
;;
autom4te*)
echo "You might have modified some maintainer files that require"
echo "the 'autom4te' program to be rebuilt."
;;
autoreconf*)
echo "You should only need it if you modified $aclocal_deps or"
echo "$automake_deps or $autoheader_deps or $automake_deps or"
echo "$configure_deps."
;;
bison*|yacc*)
echo "You should only need it if you modified a '.y' file."
echo "You may want to install the GNU Bison package:"
echo "<$gnu_software_URL/bison/>"
;;
help2man*)
echo "You should only need it if you modified a dependency" \
"of a man page."
echo "You may want to install the GNU Help2man package:"
echo "<$gnu_software_URL/help2man/>"
;;
lex*|flex*)
echo "You should only need it if you modified a '.l' file."
echo "You may want to install the Fast Lexical Analyzer package:"
echo "<$flex_URL>"
;;
makeinfo*)
echo "You should only need it if you modified a '.texi' file, or"
echo "any other file indirectly affecting the aspect of the manual."
echo "You might want to install the Texinfo package:"
echo "<$gnu_software_URL/texinfo/>"
echo "The spurious makeinfo call might also be the consequence of"
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
echo "want to install GNU make:"
echo "<$gnu_software_URL/make/>"
;;
perl*)
echo "You should only need it to run GNU Autoconf, GNU Automake, "
echo " assorted other tools, or if you modified a Perl source file."
echo "You may want to install the Perl 5 language interpreter:"
echo "<$perl_URL>"
;;
*)
echo "You might have modified some files without having the proper"
echo "tools for further handling them. Check the 'README' file, it"
echo "often tells you about the needed prerequisites for installing"
echo "this package. You may also peek at any GNU archive site, in"
echo "case some other package contains this missing '$1' program."
;;
esac
program_details "$normalized_program"
}
give_advice "$1" | sed -e '1s/^/WARNING: /' \
-e '2,$s/^/ /' >&2
# Propagate the correct exit status (expected to be 127 for a program
# not found, 63 for a program that failed due to version mismatch).
exit $st
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
+133
View File
@@ -0,0 +1,133 @@
package aux509
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"time"
)
func CreateX509SelfSignedCert(subject string, hostNames ...string) ([]byte, []byte, error) {
var err error
certPem := make([]byte, 0)
keyPem := make([]byte, 0)
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
key, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %v", err)
return certPem, keyPem, err
}
keyPemBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
keyPem = pem.EncodeToMemory(&keyPemBlock)
certSubject := pkix.Name{
CommonName: subject,
}
certIssuer := certSubject
dnsNames := make([]string, 0)
dnsNames = append(dnsNames, subject)
dnsNames = append(dnsNames, hostNames...)
tml := x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: certSubject,
Issuer: certIssuer,
DNSNames: dnsNames,
BasicConstraintsValid: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature |
x509.KeyUsageContentCommitment |
x509.KeyUsageKeyEncipherment |
x509.KeyUsageDataEncipherment,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err)
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem = pem.EncodeToMemory(&certPemBlock)
if err != nil {
return certPem, keyPem, err
}
return certPem, keyPem, err
}
func CreateX509CACert(commonName string) ([]byte, []byte, error) {
var err error
certPem := make([]byte, 0)
keyPem := make([]byte, 0)
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
key, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %v", err)
return certPem, keyPem, err
}
keyPemBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
keyPem = pem.EncodeToMemory(&keyPemBlock)
certSubject := pkix.Name{
CommonName: commonName,
}
certIssuer := certSubject
tml := x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: certSubject,
Issuer: certIssuer,
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature |
x509.KeyUsageCertSign |
x509.KeyUsageKeyEncipherment |
x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return certPem, keyPem, fmt.Errorf("Can't create a certificate: %v", err)
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem = pem.EncodeToMemory(&certPemBlock)
if err != nil {
return certPem, keyPem, err
}
return certPem, keyPem, err
}
+29
View File
@@ -0,0 +1,29 @@
package aux509
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestCert(t *testing.T) {
{
//caCert, caKey, err := CreateX509SelfSignedCert("test1")
//require.NoError(t, err)
//fmt.Println(string(caCert))
//fmt.Println(string(caKey))
}
{
caCert, caKey, err := CreateX509CACert("test1")
require.NoError(t, err)
fmt.Println(string(caCert))
fmt.Println(string(caKey))
// caCert, caKey, err = CreateX509Cert("test1", caKey)
// require.NoError(t, err)
// fmt.Println(string(caCert))
// fmt.Println(string(caKey))
}
}
+31
View File
@@ -0,0 +1,31 @@
package auxgin
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
func CorsMiddleware() gin.HandlerFunc {
headers := []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization"}
headerList := strings.Join(headers, ",")
methods := []string{"POST", "GET", "OPTIONS", "PUT", "DELETE", "UPDATE"}
methodList := strings.Join(methods, ",")
return func(gctx *gin.Context) {
gctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
gctx.Writer.Header().Set("Access-Control-Max-Age", "86400")
gctx.Writer.Header().Set("Access-Control-Allow-Methods", methodList)
gctx.Writer.Header().Set("Access-Control-Allow-Headers", headerList)
gctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
if gctx.Request.Method == "OPTIONS" {
gctx.AbortWithStatus(http.StatusOK)
} else {
gctx.Next()
}
}
}
+60
View File
@@ -0,0 +1,60 @@
package auxgin
import (
"bytes"
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func LogMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
start := time.Now()
ctx.Next()
var reqSize int64
var method string
var reqURI string
var remAddr string
if ctx.Request != nil {
reqSize = ctx.Request.ContentLength
method = ctx.Request.Method
reqURI = ctx.Request.RequestURI
remAddr = ctx.RemoteIP()
}
duration := time.Since(start).Microseconds()
var resCode int
var resSize int
if ctx.Writer != nil {
resCode = ctx.Writer.Status()
resSize = ctx.Writer.Size()
}
logString := fmt.Sprintf("%s %s %s in=%d out=%d res=%d %dms",
remAddr, method, reqURI, reqSize, resSize, resCode, duration)
logger := logrus.WithField("object", "accesslog")
logger.Infoln(logString)
}
}
type LogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (lw LogWriter) Write(data []byte) (int, error) {
lw.body.Write(data)
return lw.ResponseWriter.Write(data)
}
func (lw LogWriter) WriteString(data string) (int, error) {
lw.body.WriteString(data)
return lw.ResponseWriter.WriteString(data)
}
+34
View File
@@ -0,0 +1,34 @@
package auxgin
import (
"bytes"
"encoding/json"
"io/ioutil"
"strings"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func RequestLogMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
contentType := context.GetHeader("Content-Type")
contentType = strings.ToLower(contentType)
var requestBody []byte
if context.Request.Body != nil {
requestBody, _ = ioutil.ReadAll(context.Request.Body)
}
if strings.Contains(contentType, "application/json") && context.Request.Method == "POST" {
buffer := bytes.NewBuffer(nil)
json.Indent(buffer, requestBody, "", " ")
logger := logrus.WithField("object", "requestlog")
logger.Infoln("request:\n", buffer.String())
}
context.Request.Body = ioutil.NopCloser(bytes.NewReader(requestBody))
context.Next()
}
}
+32
View File
@@ -0,0 +1,32 @@
package auxgin
import (
"bytes"
"encoding/json"
"strings"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
func ResponseLogMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
contentType := context.GetHeader("Content-Type")
contentType = strings.ToLower(contentType)
writer := &LogWriter{
body: bytes.NewBuffer(nil),
ResponseWriter: context.Writer,
}
context.Writer = writer
context.Next()
if strings.Contains(contentType, "application/json") {
buffer := bytes.NewBuffer(nil)
json.Indent(buffer, writer.body.Bytes(), "", " ")
logger := logrus.WithField("object", "responselog")
logger.Infoln("request:\n", buffer.String())
}
}
}
+19
View File
@@ -0,0 +1,19 @@
package auxgrpc
import (
"fmt"
"google.golang.org/grpc/status"
)
func FmtError(err error) error {
if err != nil {
st, ok := status.FromError(err)
if !ok {
return err
}
err := fmt.Errorf("Return code %d, %s", st.Code(), st.Message())
return err
}
return err
}
+30
View File
@@ -0,0 +1,30 @@
package auxhttp
import (
"net/http"
"github.com/gin-gonic/gin"
)
type GenericResponse[T any] struct {
Result T `json:"result,omitempty"`
Message string `json:"message,omitempty"`
Error bool `json:"error"`
ErrorCode int64 `json:"errorCode"`
}
func SendError(c *gin.Context, err error) {
var response GenericResponse[interface{}]
response.Error = true
if err != nil {
response.Message = err.Error()
response.ErrorCode = 101
}
c.AbortWithStatusJSON(http.StatusOK, response)
}
func SendResult(c *gin.Context, result any) {
var response GenericResponse[any]
response.Result = result
c.JSON(http.StatusOK, response)
}
+57
View File
@@ -0,0 +1,57 @@
package auxhttp
import (
"errors"
"fmt"
"strings"
)
func GetBearerToken(authHeader string) (string, error) {
var err error
var res string
const bearerKey = "Bearer"
const numWords = 2
authData := strings.SplitN(authHeader, " ", numWords)
if len(authData) < numWords {
err = errors.New("Authorization key and value not found")
return res, err
}
authKey := strings.TrimSpace(authData[0])
if authKey != bearerKey {
err = fmt.Errorf("Authorization type is different from %s", bearerKey)
return res, err
}
token := authData[1]
token = strings.TrimSpace(token)
if len(token) == 0 {
return res, errors.New("Lenght of authorization token must be greater zero")
}
res = token
return res, err
}
func HaveBearerToken(authHeader string) bool {
const bearerKey = "Bearer"
const numWords = 2
authData := strings.SplitN(authHeader, " ", numWords)
if len(authData) < numWords {
return false
}
authKey := strings.TrimSpace(authData[0])
if authKey != bearerKey {
return false
}
token := authData[1]
token = strings.TrimSpace(token)
if len(token) == 0 {
return false
}
return true
}
+47
View File
@@ -0,0 +1,47 @@
package auxhttp
import (
"encoding/base64"
"errors"
"strings"
)
func ParseAuthBasicHeader(header string) (string, string, error) {
var err error
var username string
var password string
authData := strings.SplitN(header, " ", 2)
if len(authData) < 2 {
err = errors.New("Wrong authentification header")
return username, password, err
}
authType := strings.TrimSpace(authData[0])
if authType != "Basic" {
err = errors.New("Authentification type is different from basic")
return username, password, err
}
authPair := strings.TrimSpace(authData[1])
pairEncoded, err := base64.StdEncoding.DecodeString(authPair)
if err != nil {
return username, password, err
}
pair := strings.SplitN(string(pairEncoded), ":", 2)
if len(pair) < 2 {
err = errors.New("Wrong authentification pair")
return username, password, err
}
username = strings.TrimSpace(pair[0])
password = strings.TrimSpace(pair[1])
if username == "" {
err = errors.New("autentification username is null")
return username, password, err
}
if password == "" {
err = errors.New("autentification password is null")
return username, password, err
}
return username, password, err
}
+32
View File
@@ -0,0 +1,32 @@
package auxid
import (
"math/rand"
"sync"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
var (
idMtx sync.Mutex
lastID int64
)
func GenID() int64 {
// 53 bit limit for js
// See https://stackoverflow.com/questions/1379934/large-numbers-erroneously-rounded-in-javascript
idMtx.Lock()
defer idMtx.Unlock()
for {
id := (time.Now().UnixNano() / 1000) // - 10000000000000
if id != lastID {
lastID = id
return id
}
time.Sleep(1 * time.Microsecond)
}
//10467328383814
}
+111
View File
@@ -0,0 +1,111 @@
package auxpwd
import (
"bytes"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"math/rand"
"strings"
"time"
)
var rnd *rand.Rand
const (
sha256Prefix = "sha256pwd"
sha512Prefix = "sha512pwd"
saltSize = 16
)
func init() {
src := rand.NewSource(time.Now().UnixNano())
rnd = rand.New(src)
}
func MakeSHA256Hash(passwd []byte) string {
var res string
salt := hex.EncodeToString(randomBytes(saltSize))
passwdString := hex.EncodeToString(passwd)
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
hasher := sha256.New()
hasher.Write([]byte(passwdString))
checksum := hex.EncodeToString(hasher.Sum(nil))
res = fmt.Sprintf("%s:%s:%s", sha256Prefix, salt, checksum)
return res
}
func MakeSHA512Hash(passwd []byte) string {
var res string
salt := hex.EncodeToString(randomBytes(saltSize))
passwdString := hex.EncodeToString(passwd)
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
hasher := sha512.New()
hasher.Write([]byte(passwdString))
checksum := hex.EncodeToString(hasher.Sum(nil))
res = fmt.Sprintf("%s:%s:%s", sha512Prefix, salt, checksum)
return res
}
func PasswordMatchCompat(passwd []byte, hash string) bool {
if !strings.HasPrefix(hash, sha256Prefix) && !strings.HasPrefix(hash, sha512Prefix) {
if bytes.Equal(passwd, []byte(hash)) {
return true
}
return false
}
hashComponents := strings.Split(hash, ":")
if len(hashComponents) != 3 {
return false
}
method := hashComponents[0]
salt := hashComponents[1]
controlChecksum := hashComponents[2]
switch method {
case sha256Prefix:
passwdString := hex.EncodeToString(passwd)
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
hasher := sha256.New()
hasher.Write([]byte(passwdString))
checksum := hex.EncodeToString(hasher.Sum(nil))
if checksum != controlChecksum {
return false
}
case sha512Prefix:
passwdString := hex.EncodeToString(passwd)
passwdString = fmt.Sprintf("%s%s", passwdString, salt)
hasher := sha512.New()
hasher.Write([]byte(passwdString))
checksum := hex.EncodeToString(hasher.Sum(nil))
if checksum != controlChecksum {
return false
}
default:
return false
}
return true
}
func randomString(n int) string {
const letters = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
arr := make([]byte, n)
lettersArrayLen := len(letters)
for i := range arr {
arr[i] = letters[rnd.Intn(lettersArrayLen)]
}
return string(arr)
}
func randomBytes(n int) []byte {
arr := make([]byte, n)
for i := range arr {
arr[i] = byte(rnd.Intn(256) & 0xFF)
}
return arr
}
+40
View File
@@ -0,0 +1,40 @@
package auxpwd
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestPasswd256(t *testing.T) {
password := []byte("123456781")
wrongPasswd := []byte("qwerty")
hash := MakeSHA256Hash(password)
fmt.Printf("%s\n", hash)
{
match := PasswordMatchCompat(password, hash)
require.Equal(t, true, match)
}
{
match := PasswordMatchCompat(wrongPasswd, hash)
require.NotEqual(t, true, match)
}
}
func TestPasswd512(t *testing.T) {
password := []byte("123456781")
wrongPasswd := []byte("qwerty")
hash := MakeSHA512Hash(password)
fmt.Printf("%s\n", hash)
{
match := PasswordMatchCompat(password, hash)
require.Equal(t, true, match)
}
{
match := PasswordMatchCompat(wrongPasswd, hash)
require.NotEqual(t, true, match)
}
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package auxtool
import (
"os"
"path/filepath"
"strings"
)
// Clean only overbase elements of dir path if possible
func CleanDirs(basedir, datadir string) {
separator := string(os.PathSeparator)
basedir = filepath.Clean(separator + basedir)
datadir = filepath.Clean(separator + datadir)
items := strings.Split(datadir, separator)
for i := len(items); i > 0; i-- {
p := filepath.Join(items[0:i]...)
p = filepath.Clean(separator + p)
if p == basedir {
break
}
fileInfo, err := os.Stat(p)
if err != nil {
return
}
if fileInfo.IsDir() {
err = os.Remove(p)
if err != nil {
return
}
}
}
}
+31
View File
@@ -0,0 +1,31 @@
package auxtool
import (
"os"
)
func FileExists(name string) bool {
fileStat, err := os.Stat(name)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
if fileStat.IsDir() {
return false
}
return true
}
func DirExists(name string) bool {
fileStat, err := os.Stat(name)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
if fileStat == nil {
return false
}
return fileStat.IsDir()
}
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*
*
*/
/*
*/
package auxtool
import (
"math/rand"
)
func RandomString(n int) string {
const letters = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
arr := make([]byte, n)
for i := range arr {
arr[i] = letters[rand.Intn(len(letters))]
}
return string(arr)
}
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package auxtool
import (
"encoding/hex"
"fmt"
"math/rand"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func MakeTmpFilename(prefix string) string {
randBytes := make([]byte, 6)
rand.Read(randBytes)
suffix := hex.EncodeToString(randBytes)
return fmt.Sprintf("%s.tmp.%s", prefix, suffix)
}
+13
View File
@@ -0,0 +1,13 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package auxtool
import (
"time"
)
func TimeNow() string {
return time.Now().Format(time.RFC3339)
}
+27
View File
@@ -0,0 +1,27 @@
package client
import (
"context"
)
type AuthCredential struct {
Payload map[string]string
}
func NewAuthCredential(username, password string) *AuthCredential {
payload := make(map[string]string)
payload["username"] = username
payload["password"] = password
return &AuthCredential{
Payload: payload,
}
}
func (cred *AuthCredential) GetRequestMetadata(ctx context.Context, data ...string) (map[string]string, error) {
var err error
return cred.Payload, err
}
func (cred *AuthCredential) RequireTransportSecurity() bool {
return false
}
+51
View File
@@ -0,0 +1,51 @@
package client
import (
"context"
"crypto/tls"
"fmt"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"mbase/pkg/mbctl"
)
const (
DefaultPort uint32 = 1027
)
type Access struct {
Hostname string
Port uint32
Username string
Password string
}
func NewClient(access *Access) (*grpc.ClientConn, mbctl.ControlClient, error) {
var err error
var cli mbctl.ControlClient
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
const dialTimeout time.Duration = 10 * time.Second
const idleTimeout time.Duration = 30 * time.Second
authCred := NewAuthCredential(access.Username, access.Password)
dialOpts := []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
grpc.WithPerRPCCredentials(authCred),
grpc.WithBlock(),
grpc.WithIdleTimeout(idleTimeout),
}
address := fmt.Sprintf("%s:%d", access.Hostname, access.Port)
ctx, _ := context.WithTimeout(context.Background(), dialTimeout)
conn, err := grpc.DialContext(ctx, address, dialOpts...)
if err != nil {
return conn, cli, fmt.Errorf("Dial error: %v", err)
}
cli = mbctl.NewControlClient(conn)
return conn, cli, err
}
+103
View File
@@ -0,0 +1,103 @@
package cm509
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
)
func EncryptAES256(b64data string, key string) (string, error) {
var res string
var err error
const aes256KeyLen = 32
bKey := []byte(key)
keyLen := len(bKey)
switch {
case keyLen > aes256KeyLen:
bKey = bKey[:aes256KeyLen]
case keyLen < aes256KeyLen:
padding := make([]byte, aes256KeyLen-keyLen)
bKey = append(bKey, padding...)
case keyLen == 0:
return res, fmt.Errorf("Zero lenght key")
}
data, err := base64.StdEncoding.DecodeString(b64data)
if err != nil {
return res, err
}
block, err := aes.NewCipher(bKey)
if err != nil {
return res, err
}
nonce := make([]byte, 12)
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return res, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return res, err
}
ciphertext := aesgcm.Seal(nil, nonce, data, nil)
ciphertext = append(nonce, ciphertext...)
res = base64.StdEncoding.EncodeToString(ciphertext)
return res, err
}
func DecryptAES256(b64ciphertext string, key string) (string, error) {
var res string
var err error
const aes256KeyLen = 32
bKey := []byte(key)
keyLen := len(bKey)
switch {
case keyLen > aes256KeyLen:
bKey = bKey[:aes256KeyLen]
case keyLen < aes256KeyLen:
padding := make([]byte, aes256KeyLen-keyLen)
bKey = append(bKey, padding...)
case keyLen == 0:
return res, fmt.Errorf("Zero lenght key")
}
ciphertext, err := base64.StdEncoding.DecodeString(b64ciphertext)
if err != nil {
return res, err
}
block, err := aes.NewCipher(bKey)
if err != nil {
return res, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return res, err
}
nonceSize := aesgcm.NonceSize()
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return res, err
}
res = base64.StdEncoding.EncodeToString(plaintext)
return res, err
}
+35
View File
@@ -0,0 +1,35 @@
package cm509
import (
"encoding/base64"
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestAES(t *testing.T) {
var err error
src := "1234567890123456789012345678901234567890"
b64src := base64.StdEncoding.EncodeToString([]byte(src))
key := "12345678901234"
encSrc, err := EncryptAES256(b64src, key)
require.NoError(t, err)
require.NotZero(t, len(encSrc))
decSrc, err := DecryptAES256(encSrc, key)
require.NoError(t, err)
require.NotZero(t, len(decSrc))
require.Equal(t, b64src, decSrc)
dst, err := base64.StdEncoding.DecodeString(decSrc)
require.NoError(t, err)
require.Equal(t, len(src), len(dst))
require.Equal(t, string(src), string(dst))
fmt.Printf("%s\n", src)
fmt.Printf("%s\n", dst)
}
+42
View File
@@ -0,0 +1,42 @@
-----BEGIN CERTIFICATE-----
MIIHZDCCBkygAwIBAgISA0TPqlhFqMMjfL8lwr1WdKCiMA0GCSqGSIb3DQEBCwUA
MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD
EwNSMTEwHhcNMjQwNzE1MDcwNTQyWhcNMjQxMDEzMDcwNTQxWjAYMRYwFAYDVQQD
Ew1oYW0udW5peDcub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
xDw3k4983QmRIqV0PsXFfG3x1wkamrMrY9sMz+M+CR9h1iozv4OQdm5wFJp/8ert
7x+JS07v4vabYoLyVsdteRXHrqXlSDJMuZaReHIQVqKk1BYZ9miIH64ExUA6vd3r
8AmilipIPW+UJihaZnP7wPy80PUdCiq0tnSewKN+wfzka5yehWXeaTbDeDoUl1Cf
5Q3CO9KhbIYNwG8GBm+4YKiuewjjIU4sPEaCPvpvCTcwA4Lcqf1awU/nRdTLXO1e
L4LezTEPb7KSS7hEZSHs2aQbjVpoV0IcuSYI1beb7XSdsv/jDIzOpu/Sx+AqMUy+
WE4sO0Yj3ap8mbsY9HTC7QIDAQABo4IEizCCBIcwDgYDVR0PAQH/BAQDAgWgMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud
DgQWBBR3smydoErE3KVUBr0/ohKZ0ghK0jAfBgNVHSMEGDAWgBTFz0ak6vTDwHps
lcQtsF6SLybjuTBXBggrBgEFBQcBAQRLMEkwIgYIKwYBBQUHMAGGFmh0dHA6Ly9y
MTEuby5sZW5jci5vcmcwIwYIKwYBBQUHMAKGF2h0dHA6Ly9yMTEuaS5sZW5jci5v
cmcvMIICkAYDVR0RBIIChzCCAoOCEmFpcmZvcmNlLnVuaXg3Lm9yZ4IOYXJ0cy51
bml4Ny5vcmeCDWRlNC51bml4Ny5vcmeCDWRlNS51bml4Ny5vcmeCDWRlNi51bml4
Ny5vcmeCDWRlNy51bml4Ny5vcmeCDWRlYi51bml4Ny5vcmeCDWRldi51bml4Ny5v
cmeCDmRuczUudW5peDcub3Jngg1lZHUudW5peDcub3JnghBlZHVtYXgudW5peDcu
b3Jngg1naXQudW5peDcub3Jngg9nbWFpbC51bml4Ny5vcmeCDWhhbS51bml4Ny5v
cmeCDmhhc2gudW5peDcub3JnggxoZC51bml4Ny5vcmeCDmhlYXAudW5peDcub3Jn
ghJob21lZGVzay51bml4Ny5vcmeCDWh1Yi51bml4Ny5vcmeCEGl0ZGVzay51bml4
Ny5vcmeCD2xhcGlzLnVuaXg3Lm9yZ4IPbG9yZW0udW5peDcub3Jngg5tYWlsLnVu
aXg3Lm9yZ4IMbXcudW5peDcub3JnggxteC51bml4Ny5vcmeCDnBkbnMudW5peDcu
b3Jngg5waWtpLnVuaXg3Lm9yZ4IPcHJveHkudW5peDcub3JnghFyZWRtaW5lLnVu
aXg3Lm9yZ4IMcm0udW5peDcub3Jngg9zbGFjay51bml4Ny5vcmeCEHNwcmluZy51
bml4Ny5vcmeCDnNydjcudW5peDcub3JnghBzdG9yZXgudW5peDcub3JnghB0YW5h
a2gudW5peDcub3Jnggx2NS51bml4Ny5vcmeCDXcxMi51bml4Ny5vcmeCDXdjbS51
bml4Ny5vcmeCDndpa2kudW5peDcub3Jngg13d3cudW5peDcub3JnMBMGA1UdIAQM
MAowCAYGZ4EMAQIBMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcASLDja9qmRzQP
5WoC+p0w6xxSActW3SyB2bu/qznYhHMAAAGQtW0RVQAABAMASDBGAiEAjQvvzpOR
urMOiqV5g0EuAK1A9CuAKOdWJp6/s3/SKBMCIQCbTBj13lhMnTr6bSHwoBtSwoiY
aeDJzGBmDTpbgGpFdQB1AN/hVuuqBa+1nA+GcY2owDJOrlbZbqf1pWoB0cE7vlJc
AAABkLVtEicAAAQDAEYwRAIgWe0pdXXB6UmMmeSgYLDncdkS2aKHAHDdOqKOoL9x
Kx8CIBqobR/Ve1IZMTLrRN54vh8kNmF0OkVjXtrh+ste6cKUMA0GCSqGSIb3DQEB
CwUAA4IBAQA3c88cXIejS9vy4XUow6dOuud6qqkNX1osSq2vRtYkKMZb2JVuhPAr
hQsozwptJPm5lOEDQPD8676yZNVgGdjmMvA0ewdWEp9HZ7x+RFlI7RC9CqMSOWPO
p60RMiBQlK7Els38WurmW2GzZkfykzpZZ/0lIrCNjT7aB9VjGVDOjxo/xHapHSwJ
GOxq4TTU1KaFfbFl5A2F9bRVrAAWfih+DzUzhlhZBHODzadgs2CimVYIp8gEVf1j
dGetiU2j+cvOpvQnfX7Xr8Cf2YK7E0j6lfC3RPvK9oA1bP5KBMWELXsGBrwIxgUB
f2/jRcxrQoGoEYwYW4D/JViKwelABzfc
-----END CERTIFICATE-----
+29
View File
@@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIFBjCCAu6gAwIBAgIRAIp9PhPWLzDvI4a9KQdrNPgwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
WhcNMjcwMzEyMjM1OTU5WjAzMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDEMMAoGA1UEAxMDUjExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAuoe8XBsAOcvKCs3UZxD5ATylTqVhyybKUvsVAbe5KPUoHu0nsyQYOWcJ
DAjs4DqwO3cOvfPlOVRBDE6uQdaZdN5R2+97/1i9qLcT9t4x1fJyyXJqC4N0lZxG
AGQUmfOx2SLZzaiSqhwmej/+71gFewiVgdtxD4774zEJuwm+UE1fj5F2PVqdnoPy
6cRms+EGZkNIGIBloDcYmpuEMpexsr3E+BUAnSeI++JjF5ZsmydnS8TbKF5pwnnw
SVzgJFDhxLyhBax7QG0AtMJBP6dYuC/FXJuluwme8f7rsIU5/agK70XEeOtlKsLP
Xzze41xNG/cLJyuqC0J3U095ah2H2QIDAQABo4H4MIH1MA4GA1UdDwEB/wQEAwIB
hjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB
/wIBADAdBgNVHQ4EFgQUxc9GpOr0w8B6bJXELbBeki8m47kwHwYDVR0jBBgwFoAU
ebRZ5nu25eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAC
hhZodHRwOi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcG
A1UdHwQgMB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcN
AQELBQADggIBAE7iiV0KAxyQOND1H/lxXPjDj7I3iHpvsCUf7b632IYGjukJhM1y
v4Hz/MrPU0jtvfZpQtSlET41yBOykh0FX+ou1Nj4ScOt9ZmWnO8m2OG0JAtIIE38
01S0qcYhyOE2G/93ZCkXufBL713qzXnQv5C/viOykNpKqUgxdKlEC+Hi9i2DcaR1
e9KUwQUZRhy5j/PEdEglKg3l9dtD4tuTm7kZtB8v32oOjzHTYw+7KdzdZiw/sBtn
UfhBPORNuay4pJxmY/WrhSMdzFO2q3Gu3MUBcdo27goYKjL9CTF8j/Zz55yctUoV
aneCWs/ajUX+HypkBTA+c8LGDLnWO2NKq0YD/pnARkAnYGPfUDoHR9gVSp/qRx+Z
WghiDLZsMwhN1zjtSC0uBWiugF3vTNzYIEFfaPG7Ws3jDrAMMYebQ95JQ+HIBD/R
PBuHRTBpqKlyDnkSHDHYPiNX3adPoPAcgdF3H2/W0rmoswMWgTlLn1Wu0mrks7/q
pdWfS6PJ1jty80r2VKsM/Dj3YIDfbjXKdaFU5C+8bhfJGqU3taKauuz0wHVGT3eo
6FlWkWYtbt4pgdamlwVeZEW+LM7qZEJEsMNPrfC03APKmZsJgpWCDWOKZvkZcvjV
uYkQ4omYCTX5ohy+knMjdOmdH9c7SpqEWBDC86fiNex+O0XOMEZSa8DA
-----END CERTIFICATE-----
+31
View File
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----
+31
View File
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFVDCCBDygAwIBAgIRAKLBiJX85huxZJB/Ylc2Y5gwDQYJKoZIhvcNAQEFBQAw
gakxCzAJBgNVBAYTAnVzMQ0wCwYDVQQIEwRVdGFoMRcwFQYDVQQHEw5TYWx0IExh
a2UgQ2l0eTEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
DwYDVQQLEwhEU1RDQSBYMTEWMBQGA1UEAxMNRFNUIFJvb3RDQSBYMTEhMB8GCSqG
SIb3DQEJARYSY2FAZGlnc2lndHJ1c3QuY29tMB4XDTA0MDkwODE0NDM0NVoXDTA4
MTEyODEzMDI1OVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qg
Q28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+Do
M3ZJKuM/IUmTrE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwG
MoOifooUMM0RoOEqOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOe
DNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqc
kh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr
2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaOCAd4wggHaMA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMIG7BgNVHR8EgbMwgbAwga2ggaqg
gaeGgaRsZGFwOi8vbGRhcC5kaWdzaWd0cnVzdC5jb20vY249RFNUJTIwUm9vdENB
JTIwWDEsb3U9RFNUQ0ElMjBYMSxvPURpZ2l0YWwlMjBTaWduYXR1cmUlMjBUcnVz
dCUyMENvLixsPVNhbHQlMjBMYWtlJTIwQ2l0eSxTPVV0YWgsYz11cz9jZXJ0aWZp
Y2F0ZVJldm9jYXRpb25MaXN0O2JpbmFyeTCBuAYIKwYBBQUHAQEEgaswgagwgaUG
CCsGAQUFBzAChoGYbGRhcDovL2xkYXAuZGlnc2lndHJ1c3QuY29tL2NuPURTVCUy
MFJvb3RDQSUyMFgxLG91PURTVENBJTIwWDEsbz1EaWdpdGFsJTIwU2lnbmF0dXJl
JTIwVHJ1c3QlMjBDby4sbD1TYWx0JTIwTGFrZSUyMENpdHksUz1VdGFoLGM9dXM/
Y0FDZXJ0aWZpY2F0ZTtiaW5hcnkwHwYDVR0jBBgwFoAUaU2asPSCd8A2GzVVCRQa
/goSAAowHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqGSIb3DQEB
BQUAA4IBAQCpFXMtsChLFvN/Z+mwiodooamIW0qjMoVGHeN9CcDdUIGQI7cgbT10
tsJuEZx3opp2s4LoM7Gn/o0T/rysLlT34vPwI4Ei/df3aG0ite5ehqWgMuc65n1P
tadwl5JFFx3l8B0YWrOv5xJ0kY+br4FGI2OGqxagBtH2y7Uak4Iq2xipTHhlvx6a
DWAzGQHovRdLf1c4cFti11gU29QYPgDXsJSuriq3xcItGzjYZT9V45WiIlmdvez0
vCr6wcnjifctVfQ7z2o2+yl7A1+ijNhDhyfcrdPqctwx0Nk8IDitss+vNMsqoEHx
uBoIoQw0DQhKAEYri9bw+9wqZhotOjMw
-----END CERTIFICATE-----
+480
View File
@@ -0,0 +1,480 @@
package cm509
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"fmt"
"math/big"
"net"
"time"
)
func DoubleEncodedCertKeyMatch(cert, key string) error {
var err error
certPEM, err := base64.StdEncoding.DecodeString(cert)
if err != nil {
return err
}
keyPEM, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return err
}
_, err = tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return err
}
return err
}
type CreateIssuerPairParams struct {
OrganizationName string
OrganizationalUnitName string
CommonName string
SignerCert string
SignerKey string
SerialNumber int64
}
type CreateIssuerPairResult struct {
Name string
Cert string
Key string
}
func CreateIssuerPair(params *CreateIssuerPairParams) (*CreateIssuerPairResult, error) {
var err error
res := &CreateIssuerPairResult{}
if params.SignerKey != "" && params.SignerCert == "" {
err = fmt.Errorf("The signature key and certificate must be defined together")
return res, err
}
if params.SignerKey == "" && params.SignerCert != "" {
err = fmt.Errorf("The signature key and certificate must be defined together")
return res, err
}
var signerKey any
if params.SignerKey != "" {
signerKey, err = ParseDoubleEncodedKey(params.SignerKey)
if err != nil {
return res, err
}
}
var signerCert *x509.Certificate
if params.SignerCert != "" {
signerCert, err = ParseDoubleEncodedCerificate(params.SignerCert)
if err != nil {
return res, err
}
}
certPem := make([]byte, 0)
keyPem := make([]byte, 0)
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
certKey, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %v", err)
return res, err
}
keyPemBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certKey),
}
keyPem = pem.EncodeToMemory(keyPemBlock)
certSubject := pkix.Name{
CommonName: params.CommonName,
}
if params.OrganizationName != "" {
certSubject.Organization = []string{params.OrganizationName}
}
if params.OrganizationalUnitName != "" {
certSubject.OrganizationalUnit = []string{params.OrganizationalUnitName}
}
certIssuer := certSubject
if signerCert != nil {
certIssuer = signerCert.Subject
}
var issuerKey any = certKey
if signerKey != nil {
issuerKey = signerKey
}
res.Name = certSubject.String()
serialNumber := big.NewInt(now.UnixNano())
if params.SerialNumber != 0 {
serialNumber = big.NewInt(params.SerialNumber)
}
certTempl := &x509.Certificate{
SerialNumber: serialNumber,
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: certSubject,
Issuer: certIssuer,
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign |
x509.KeyUsageKeyEncipherment | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
}
parentCert := certTempl
if signerCert != nil {
parentCert = signerCert
}
certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, parentCert, &certKey.PublicKey, issuerKey)
if err != nil {
err := fmt.Errorf("Can't create a certificate: %v", err)
return res, err
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem = pem.EncodeToMemory(&certPemBlock)
if err != nil {
return res, err
}
res.Cert = base64.StdEncoding.EncodeToString(certPem)
res.Key = base64.StdEncoding.EncodeToString(keyPem)
return res, err
}
type CreateServicePairParams struct {
OrganizationName string
OrganizationalUnitName string
CommonName string
DNSNames []string
IPAddresses []string
IssuerKey string
IssuerCert string
SerialNumber int64
}
type CreateServicePairResult struct {
Name string
Cert string
Key string
}
func CreateServicePair(params *CreateServicePairParams) (*CreateServicePairResult, error) {
var err error
res := &CreateServicePairResult{}
if params.IssuerKey != "" && params.IssuerCert == "" {
err = fmt.Errorf("The signature key and certificate must be defined together")
return res, err
}
if params.IssuerKey == "" && params.IssuerCert != "" {
err = fmt.Errorf("The signature key and certificate must be defined together")
return res, err
}
var signerKey any
if params.IssuerKey != "" {
signerKey, err = ParseDoubleEncodedKey(params.IssuerKey)
if err != nil {
return res, err
}
}
var signerCert *x509.Certificate
if params.IssuerCert != "" {
signerCert, err = ParseDoubleEncodedCerificate(params.IssuerCert)
if err != nil {
return res, err
}
}
certPem := make([]byte, 0)
keyPem := make([]byte, 0)
now := time.Now()
const yearsAfter int = 10
const keySize int = 2048
certKey, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
err := fmt.Errorf("Can't create a private key: %v", err)
return res, err
}
keyPemBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certKey),
}
keyPem = pem.EncodeToMemory(keyPemBlock)
certSubject := pkix.Name{
CommonName: params.CommonName,
}
if params.OrganizationName != "" {
certSubject.Organization = []string{params.OrganizationName}
}
if params.OrganizationalUnitName != "" {
certSubject.OrganizationalUnit = []string{params.OrganizationalUnitName}
}
certIssuer := certSubject
if signerCert != nil {
certIssuer = signerCert.Subject
}
var issuerKey any = certKey
if signerKey != nil {
issuerKey = signerKey
}
res.Name = certSubject.String()
var netAddresses []net.IP
if params.IPAddresses != nil && len(params.IPAddresses) > 0 {
netAddresses = make([]net.IP, 0)
for _, ipAddress := range params.IPAddresses {
netAddress := net.ParseIP(ipAddress)
netAddresses = append(netAddresses, netAddress)
}
}
var dnsNames []string
if params.DNSNames != nil && len(params.DNSNames) > 0 {
dnsNames = make([]string, 0)
dnsNames = append(dnsNames, params.DNSNames...)
}
serialNumber := big.NewInt(now.UnixNano())
if params.SerialNumber != 0 {
serialNumber = big.NewInt(params.SerialNumber)
}
certTempl := &x509.Certificate{
SerialNumber: serialNumber,
NotBefore: now,
NotAfter: now.AddDate(yearsAfter, 0, 0),
Subject: certSubject,
Issuer: certIssuer,
DNSNames: dnsNames,
IPAddresses: netAddresses,
IsCA: false,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
parentCert := certTempl
if signerCert != nil {
parentCert = signerCert
}
certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, parentCert, &certKey.PublicKey, issuerKey)
if err != nil {
err := fmt.Errorf("Can't create a certificate: %v", err)
return res, err
}
certPemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
certPem = pem.EncodeToMemory(&certPemBlock)
if err != nil {
return res, err
}
res.Cert = base64.StdEncoding.EncodeToString(certPem)
res.Key = base64.StdEncoding.EncodeToString(keyPem)
return res, err
}
func ParseDoubleEncodedCerificate(certString string) (*x509.Certificate, error) {
var err error
res := &x509.Certificate{}
certPEM, err := base64.StdEncoding.DecodeString(certString)
if err != nil {
err := fmt.Errorf("Failed to parse base64 certificate string: %v", err)
return res, err
}
certBlock, _ := pem.Decode([]byte(certPEM))
if certBlock == nil {
err := fmt.Errorf("Failed to parse certificate PEM")
return res, err
}
if certBlock.Type != "CERTIFICATE" {
err := fmt.Errorf("Unknown PEM certificate type: %s", certBlock.Type)
return res, err
}
if len(certBlock.Bytes) == 0 {
err := fmt.Errorf("Empty PEM certificate block")
return res, err
}
res, err = x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return res, err
}
return res, err
}
func ParseEncodedCerificate(certPEM string) (*x509.Certificate, error) {
var err error
res := &x509.Certificate{}
certBlock, _ := pem.Decode([]byte(certPEM))
if certBlock == nil {
err := fmt.Errorf("Failed to parse certificate PEM")
return res, err
}
if certBlock.Type != "CERTIFICATE" {
err := fmt.Errorf("Unknown PEM certificate type: %s", certBlock.Type)
return res, err
}
if len(certBlock.Bytes) == 0 {
err := fmt.Errorf("Empty PEM certificate block")
return res, err
}
res, err = x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return res, err
}
return res, err
}
func ParseDoubleEncodedKey(keyString string) (any, error) {
var err error
var res any
keyPEM, err := base64.StdEncoding.DecodeString(keyString)
if err != nil {
err := fmt.Errorf("Failed to parse base64 key string: %v", err)
return res, err
}
keyBlock, _ := pem.Decode([]byte(keyPEM))
if keyBlock == nil {
err := fmt.Errorf("Failed to parse key PEM")
return res, err
}
switch keyBlock.Type {
case "PRIVATE KEY":
res, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
if err != nil {
return res, err
}
case "RSA PRIVATE KEY":
res, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return res, err
}
case "EC PRIVATE KEY":
res, err = x509.ParseECPrivateKey(keyBlock.Bytes)
if err != nil {
return res, err
}
default:
err := fmt.Errorf("Unknown PEM key type: %s", keyBlock.Type)
return res, err
}
return res, err
}
func ParseEncodedKey(keyPEM string) (any, error) {
var err error
var res any
keyBlock, _ := pem.Decode([]byte(keyPEM))
if keyBlock == nil {
err := fmt.Errorf("Failed to parse key PEM")
return res, err
}
switch keyBlock.Type {
case "PRIVATE KEY":
res, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
if err != nil {
return res, err
}
case "RSA PRIVATE KEY":
res, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return res, err
}
case "EC PRIVATE KEY":
res, err = x509.ParseECPrivateKey(keyBlock.Bytes)
if err != nil {
return res, err
}
default:
err := fmt.Errorf("Unknown PEM key type: %s", keyBlock.Type)
return res, err
}
return res, err
}
func CheckDoubleEncodedCertificateChain(topIssuerCN string, certStrings []string) ([]string, error) {
var err error
res := make([]string, 0)
certObjs := make([]*x509.Certificate, 0)
for _, certString := range certStrings {
certObj, err := ParseDoubleEncodedCerificate(certString)
if err != nil {
return res, err
}
certObjs = append(certObjs, certObj)
}
issuerFound := false
issuerIndex := -1
for i, certObj := range certObjs {
if topIssuerCN == certObj.Subject.String() {
issuerIndex = i
issuerFound = true
}
}
if !issuerFound {
err := fmt.Errorf("Issuer for %s cannot found", topIssuerCN)
return res, err
}
interCertObj := certObjs[issuerIndex]
interCertString := certStrings[issuerIndex]
if !interCertObj.IsCA {
err := fmt.Errorf("Issuer %s is not CA", interCertObj.Subject.String())
return res, err
}
expired := interCertObj.NotAfter.Before(time.Now())
if !expired {
err := fmt.Errorf("Issuer %s expired %v", interCertObj.Subject.String(), interCertObj.NotAfter)
return res, err
}
res = append(res, interCertString)
if interCertObj.Subject.String() == interCertObj.Issuer.String() {
return res, err
}
updatedCertStrings := append(certStrings[:issuerIndex], certStrings[issuerIndex+1:]...)
topIssuerCN = interCertObj.Issuer.String()
certStringsTail, err := CheckDoubleEncodedCertificateChain(topIssuerCN, updatedCertStrings)
if err != nil {
return res, err
}
res = append(res, certStringsTail...)
return res, err
}
+48
View File
@@ -0,0 +1,48 @@
package cm509
import (
"encoding/base64"
"fmt"
"os"
"strings"
"testing"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
)
func TestCertChainCheckerErr(t *testing.T) {
var err error
certBytes, err := os.ReadFile("testchain_a01.crt")
require.NoError(t, err)
require.NotZero(t, len(certBytes))
certObj, err := ParseEncodedCerificate(string(certBytes))
require.NoError(t, err)
require.NotNil(t, certObj)
certStrings := make([]string, 0)
for i := 1; i < 4; i++ {
certBytes, err := os.ReadFile(fmt.Sprintf("testchain_a%02d.crt", i))
require.NoError(t, err)
require.NotZero(t, len(certBytes))
certString := base64.StdEncoding.EncodeToString(certBytes)
certStrings = append(certStrings, certString)
}
topIssuerCN := certObj.Issuer.String()
_, err = CheckDoubleEncodedCertificateChain(topIssuerCN, certStrings)
require.Error(t, err)
//require.NotNil(t, resString)
//require.NotZero(t, len(resString))
}
func printObj(label string, obj any) {
objBytes, _ := yaml.Marshal(obj)
objString := string(objBytes)
if strings.Count(objString, "\n") < 2 {
fmt.Printf("==== %s: %s\n", label, objString)
} else {
fmt.Printf("==== %s ::\n%s\n", label, objString)
}
}
+1
View File
@@ -0,0 +1 @@
package common
+78
View File
@@ -0,0 +1,78 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package logger
import (
"bytes"
"fmt"
"io"
"os"
"sync"
"time"
)
var (
mtx sync.Mutex
output io.WriteCloser = os.Stderr
)
type Logger struct {
subject string
writer io.WriteCloser
mtx *sync.Mutex
}
func NewLogger(subj string) *Logger {
return &Logger{
subject: subj,
writer: output,
mtx: &mtx,
}
}
func SetWriter(newOut io.WriteCloser) {
mtx.Lock()
output = newOut
mtx.Unlock()
}
func (logg *Logger) SetWriter(newOut io.WriteCloser) {
mtx.Lock()
logg.writer = newOut
var newMtx sync.Mutex
logg.mtx = &newMtx
mtx.Unlock()
}
func (logg *Logger) Debugf(message string, args ...any) {
logg.printf("debug", message, args...)
}
func (logg *Logger) Infof(message string, args ...any) {
logg.printf("info", message, args...)
}
func (logg *Logger) Warningf(message string, args ...any) {
logg.printf("warning", message, args...)
}
func (logg *Logger) Errorf(message string, args ...any) {
logg.printf("error", message, args...)
}
func (logg *Logger) printf(level, message string, args ...any) {
timestamp := time.Now().Format(time.RFC3339)
buffer := bytes.NewBuffer([]byte{})
if logg.subject != "" {
fmt.Fprintf(buffer, "%s %s.%s: ", timestamp, logg.subject, level)
} else {
fmt.Fprintf(buffer, "%s %s: ", timestamp, level)
}
fmt.Fprintf(buffer, message, args...)
fmt.Fprintf(buffer, "\n")
logg.mtx.Lock()
fmt.Fprint(output, buffer.String())
logg.mtx.Unlock()
}
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package logger
import (
"testing"
"os"
"github.com/stretchr/testify/require"
)
func TestLogger(t *testing.T) {
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666)
require.NoError(t, err)
SetWriter(devNull)
logg := NewLogger("test")
logg.Debugf("foo: %s", "bar")
}
func BenchmarkLoggerL(b *testing.B) {
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666)
require.NoError(b, err)
SetWriter(devNull)
logg := NewLogger("test")
for i := 0; i < b.N; i++ {
logg.Debugf("foo: %s", "bar")
}
}
func BenchmarkLoggerP(b *testing.B) {
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666)
require.NoError(b, err)
logg := NewLogger("test")
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logg.Debugf("foo: %s", "bar")
}
})
}
File diff suppressed because it is too large Load Diff
+452
View File
@@ -0,0 +1,452 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v3.21.12
// source: mbctl.proto
package mbctl
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.62.0 or later.
const _ = grpc.SupportPackageIsVersion8
const (
Control_GetHello_FullMethodName = "/mbasecontrol.Control/getHello"
Control_CreateAccount_FullMethodName = "/mbasecontrol.Control/createAccount"
Control_DeleteAccount_FullMethodName = "/mbasecontrol.Control/deleteAccount"
Control_UpdateAccount_FullMethodName = "/mbasecontrol.Control/updateAccount"
Control_GetAccount_FullMethodName = "/mbasecontrol.Control/getAccount"
Control_ListAccounts_FullMethodName = "/mbasecontrol.Control/listAccounts"
Control_SetGrant_FullMethodName = "/mbasecontrol.Control/setGrant"
Control_DeleteGrant_FullMethodName = "/mbasecontrol.Control/deleteGrant"
Control_GetDump_FullMethodName = "/mbasecontrol.Control/getDump"
Control_RestoreDump_FullMethodName = "/mbasecontrol.Control/restoreDump"
)
// ControlClient is the client API for Control service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ControlClient interface {
GetHello(ctx context.Context, in *GetHelloParams, opts ...grpc.CallOption) (*GetHelloResult, error)
CreateAccount(ctx context.Context, in *CreateAccountParams, opts ...grpc.CallOption) (*CreateAccountResult, error)
DeleteAccount(ctx context.Context, in *DeleteAccountParams, opts ...grpc.CallOption) (*DeleteAccountResult, error)
UpdateAccount(ctx context.Context, in *UpdateAccountParams, opts ...grpc.CallOption) (*UpdateAccountResult, error)
GetAccount(ctx context.Context, in *GetAccountParams, opts ...grpc.CallOption) (*GetAccountResult, error)
ListAccounts(ctx context.Context, in *ListAccountsParams, opts ...grpc.CallOption) (*ListAccountsResult, error)
SetGrant(ctx context.Context, in *SetGrantParams, opts ...grpc.CallOption) (*SetGrantResult, error)
DeleteGrant(ctx context.Context, in *DeleteGrantParams, opts ...grpc.CallOption) (*DeleteGrantResult, error)
GetDump(ctx context.Context, in *GetDumpParams, opts ...grpc.CallOption) (*GetDumpResult, error)
RestoreDump(ctx context.Context, in *RestoreDumpParams, opts ...grpc.CallOption) (*RestoreDumpResult, error)
}
type controlClient struct {
cc grpc.ClientConnInterface
}
func NewControlClient(cc grpc.ClientConnInterface) ControlClient {
return &controlClient{cc}
}
func (c *controlClient) GetHello(ctx context.Context, in *GetHelloParams, opts ...grpc.CallOption) (*GetHelloResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetHelloResult)
err := c.cc.Invoke(ctx, Control_GetHello_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) CreateAccount(ctx context.Context, in *CreateAccountParams, opts ...grpc.CallOption) (*CreateAccountResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateAccountResult)
err := c.cc.Invoke(ctx, Control_CreateAccount_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) DeleteAccount(ctx context.Context, in *DeleteAccountParams, opts ...grpc.CallOption) (*DeleteAccountResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteAccountResult)
err := c.cc.Invoke(ctx, Control_DeleteAccount_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) UpdateAccount(ctx context.Context, in *UpdateAccountParams, opts ...grpc.CallOption) (*UpdateAccountResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdateAccountResult)
err := c.cc.Invoke(ctx, Control_UpdateAccount_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) GetAccount(ctx context.Context, in *GetAccountParams, opts ...grpc.CallOption) (*GetAccountResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetAccountResult)
err := c.cc.Invoke(ctx, Control_GetAccount_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) ListAccounts(ctx context.Context, in *ListAccountsParams, opts ...grpc.CallOption) (*ListAccountsResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListAccountsResult)
err := c.cc.Invoke(ctx, Control_ListAccounts_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) SetGrant(ctx context.Context, in *SetGrantParams, opts ...grpc.CallOption) (*SetGrantResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetGrantResult)
err := c.cc.Invoke(ctx, Control_SetGrant_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) DeleteGrant(ctx context.Context, in *DeleteGrantParams, opts ...grpc.CallOption) (*DeleteGrantResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteGrantResult)
err := c.cc.Invoke(ctx, Control_DeleteGrant_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) GetDump(ctx context.Context, in *GetDumpParams, opts ...grpc.CallOption) (*GetDumpResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetDumpResult)
err := c.cc.Invoke(ctx, Control_GetDump_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlClient) RestoreDump(ctx context.Context, in *RestoreDumpParams, opts ...grpc.CallOption) (*RestoreDumpResult, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RestoreDumpResult)
err := c.cc.Invoke(ctx, Control_RestoreDump_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ControlServer is the server API for Control service.
// All implementations must embed UnimplementedControlServer
// for forward compatibility
type ControlServer interface {
GetHello(context.Context, *GetHelloParams) (*GetHelloResult, error)
CreateAccount(context.Context, *CreateAccountParams) (*CreateAccountResult, error)
DeleteAccount(context.Context, *DeleteAccountParams) (*DeleteAccountResult, error)
UpdateAccount(context.Context, *UpdateAccountParams) (*UpdateAccountResult, error)
GetAccount(context.Context, *GetAccountParams) (*GetAccountResult, error)
ListAccounts(context.Context, *ListAccountsParams) (*ListAccountsResult, error)
SetGrant(context.Context, *SetGrantParams) (*SetGrantResult, error)
DeleteGrant(context.Context, *DeleteGrantParams) (*DeleteGrantResult, error)
GetDump(context.Context, *GetDumpParams) (*GetDumpResult, error)
RestoreDump(context.Context, *RestoreDumpParams) (*RestoreDumpResult, error)
mustEmbedUnimplementedControlServer()
}
// UnimplementedControlServer must be embedded to have forward compatible implementations.
type UnimplementedControlServer struct {
}
func (UnimplementedControlServer) GetHello(context.Context, *GetHelloParams) (*GetHelloResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetHello not implemented")
}
func (UnimplementedControlServer) CreateAccount(context.Context, *CreateAccountParams) (*CreateAccountResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAccount not implemented")
}
func (UnimplementedControlServer) DeleteAccount(context.Context, *DeleteAccountParams) (*DeleteAccountResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteAccount not implemented")
}
func (UnimplementedControlServer) UpdateAccount(context.Context, *UpdateAccountParams) (*UpdateAccountResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateAccount not implemented")
}
func (UnimplementedControlServer) GetAccount(context.Context, *GetAccountParams) (*GetAccountResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAccount not implemented")
}
func (UnimplementedControlServer) ListAccounts(context.Context, *ListAccountsParams) (*ListAccountsResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAccounts not implemented")
}
func (UnimplementedControlServer) SetGrant(context.Context, *SetGrantParams) (*SetGrantResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetGrant not implemented")
}
func (UnimplementedControlServer) DeleteGrant(context.Context, *DeleteGrantParams) (*DeleteGrantResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteGrant not implemented")
}
func (UnimplementedControlServer) GetDump(context.Context, *GetDumpParams) (*GetDumpResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDump not implemented")
}
func (UnimplementedControlServer) RestoreDump(context.Context, *RestoreDumpParams) (*RestoreDumpResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method RestoreDump not implemented")
}
func (UnimplementedControlServer) mustEmbedUnimplementedControlServer() {}
// UnsafeControlServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ControlServer will
// result in compilation errors.
type UnsafeControlServer interface {
mustEmbedUnimplementedControlServer()
}
func RegisterControlServer(s grpc.ServiceRegistrar, srv ControlServer) {
s.RegisterService(&Control_ServiceDesc, srv)
}
func _Control_GetHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetHelloParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).GetHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_GetHello_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).GetHello(ctx, req.(*GetHelloParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_CreateAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateAccountParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).CreateAccount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_CreateAccount_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).CreateAccount(ctx, req.(*CreateAccountParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_DeleteAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteAccountParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).DeleteAccount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_DeleteAccount_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).DeleteAccount(ctx, req.(*DeleteAccountParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_UpdateAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateAccountParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).UpdateAccount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_UpdateAccount_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).UpdateAccount(ctx, req.(*UpdateAccountParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_GetAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetAccountParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).GetAccount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_GetAccount_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).GetAccount(ctx, req.(*GetAccountParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_ListAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAccountsParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).ListAccounts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_ListAccounts_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).ListAccounts(ctx, req.(*ListAccountsParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_SetGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetGrantParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).SetGrant(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_SetGrant_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).SetGrant(ctx, req.(*SetGrantParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_DeleteGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteGrantParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).DeleteGrant(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_DeleteGrant_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).DeleteGrant(ctx, req.(*DeleteGrantParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_GetDump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetDumpParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).GetDump(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_GetDump_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).GetDump(ctx, req.(*GetDumpParams))
}
return interceptor(ctx, in, info, handler)
}
func _Control_RestoreDump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RestoreDumpParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServer).RestoreDump(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Control_RestoreDump_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServer).RestoreDump(ctx, req.(*RestoreDumpParams))
}
return interceptor(ctx, in, info, handler)
}
// Control_ServiceDesc is the grpc.ServiceDesc for Control service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Control_ServiceDesc = grpc.ServiceDesc{
ServiceName: "mbasecontrol.Control",
HandlerType: (*ControlServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "getHello",
Handler: _Control_GetHello_Handler,
},
{
MethodName: "createAccount",
Handler: _Control_CreateAccount_Handler,
},
{
MethodName: "deleteAccount",
Handler: _Control_DeleteAccount_Handler,
},
{
MethodName: "updateAccount",
Handler: _Control_UpdateAccount_Handler,
},
{
MethodName: "getAccount",
Handler: _Control_GetAccount_Handler,
},
{
MethodName: "listAccounts",
Handler: _Control_ListAccounts_Handler,
},
{
MethodName: "setGrant",
Handler: _Control_SetGrant_Handler,
},
{
MethodName: "deleteGrant",
Handler: _Control_DeleteGrant_Handler,
},
{
MethodName: "getDump",
Handler: _Control_GetDump_Handler,
},
{
MethodName: "restoreDump",
Handler: _Control_RestoreDump_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "mbctl.proto",
}
+190
View File
@@ -0,0 +1,190 @@
package netacl
import (
"encoding/json"
"errors"
"fmt"
"net/netip"
"sigs.k8s.io/yaml"
)
type NetACL struct {
enabledAddresses []netip.Addr
enabledNetworks []netip.Prefix
disabledAddresses []netip.Addr
disabledNetworks []netip.Prefix
}
func NewNetACL() *NetACL {
return &NetACL{
enabledAddresses: make([]netip.Addr, 0),
enabledNetworks: make([]netip.Prefix, 0),
disabledAddresses: make([]netip.Addr, 0),
disabledNetworks: make([]netip.Prefix, 0),
}
}
type netACL struct {
EnabledAddresses []string `json:"enabledAddresses" yaml:"enabledAddresses"`
EnabledNetworks []string `json:"enabledNetworks" yaml:"enabledNetworks"`
DisabledAddresses []string `json:"disabledAddresses" yaml:"disabledAddresses"`
DisabledNetworks []string `json:"disabledNetworks" yaml:"disabledNetworks"`
}
func newNetACL() *netACL {
return &netACL{
EnabledAddresses: make([]string, 0),
EnabledNetworks: make([]string, 0),
DisabledAddresses: make([]string, 0),
DisabledNetworks: make([]string, 0),
}
}
func (al *NetACL) MarshalJSON() ([]byte, error) {
alDescr := newNetACL()
for _, disabledAddress := range al.disabledAddresses {
alDescr.DisabledAddresses = append(alDescr.DisabledAddresses, disabledAddress.String())
}
for _, disabledNetwork := range al.disabledNetworks {
alDescr.DisabledNetworks = append(alDescr.DisabledNetworks, disabledNetwork.String())
}
for _, enabledAddress := range al.enabledAddresses {
alDescr.EnabledAddresses = append(alDescr.EnabledAddresses, enabledAddress.String())
}
for _, enabledNetwork := range al.enabledNetworks {
alDescr.EnabledNetworks = append(alDescr.EnabledNetworks, enabledNetwork.String())
}
return json.Marshal(alDescr)
}
func (al *NetACL) UnmarshalJSON(data []byte) error {
var err error
return err
}
func (al *NetACL) MarshalYAML() ([]byte, error) {
alDescr := newNetACL()
for _, disabledAddress := range al.disabledAddresses {
alDescr.DisabledAddresses = append(alDescr.DisabledAddresses, disabledAddress.String())
}
for _, disabledNetwork := range al.disabledNetworks {
alDescr.DisabledNetworks = append(alDescr.DisabledNetworks, disabledNetwork.String())
}
for _, enabledAddress := range al.enabledAddresses {
alDescr.EnabledAddresses = append(alDescr.EnabledAddresses, enabledAddress.String())
}
for _, enabledNetwork := range al.enabledNetworks {
alDescr.EnabledNetworks = append(alDescr.EnabledNetworks, enabledNetwork.String())
}
return yaml.Marshal(alDescr)
}
func (al *NetACL) UnmarshalYAML(data []byte) error {
var err error
return err
}
func (al *NetACL) AddressIsEnabled(address string) (bool, error) {
var err error
var res bool
addr, err := netip.ParseAddr(address)
if err != nil {
return res, err
}
for _, disabledAddr := range al.disabledAddresses {
if disabledAddr.Compare(addr) == 0 {
res = false
return res, err
}
}
for _, disabledNetwork := range al.disabledNetworks {
if disabledNetwork.Contains(addr) {
res = false
return res, err
}
}
for _, enabledAddr := range al.enabledAddresses {
if enabledAddr.Compare(addr) == 0 {
res = true
return res, err
}
}
for _, enabledNetwork := range al.enabledNetworks {
if enabledNetwork.Contains(addr) {
res = true
return res, err
}
}
return res, err
}
func (al *NetACL) AddrIsEnabled(addr netip.Addr) (bool, error) {
var err error
var res bool
for _, disabledAddr := range al.disabledAddresses {
if disabledAddr.Compare(addr) == 0 {
res = false
return res, err
}
}
for _, disabledNetwork := range al.disabledNetworks {
if disabledNetwork.Contains(addr) {
res = false
return res, err
}
}
for _, enabledAddr := range al.enabledAddresses {
if enabledAddr.Compare(addr) == 0 {
res = true
return res, err
}
}
for _, enabledNetwork := range al.enabledNetworks {
if enabledNetwork.Contains(addr) {
res = true
return res, err
}
}
return res, err
}
func (al *NetACL) AddEnabledAddresses(addresses ...string) error {
var err error
for _, address := range addresses {
addr, addrParseErr := netip.ParseAddr(address)
if addrParseErr == nil {
al.enabledAddresses = append(al.enabledAddresses, addr)
continue
}
prefix, prefixParseErr := netip.ParsePrefix(address)
if prefixParseErr == nil {
al.enabledNetworks = append(al.enabledNetworks, prefix)
continue
}
err = errors.Join(addrParseErr, prefixParseErr)
err = fmt.Errorf("Address %s is not correct, error: %v", address, err)
break
}
return err
}
func (al *NetACL) AddDisabledAddresses(addresses ...string) error {
var err error
for _, address := range addresses {
addr, addrParseErr := netip.ParseAddr(address)
if addrParseErr == nil {
al.disabledAddresses = append(al.disabledAddresses, addr)
continue
}
prefix, prefixParseErr := netip.ParsePrefix(address)
if prefixParseErr == nil {
al.disabledNetworks = append(al.disabledNetworks, prefix)
continue
}
err = errors.Join(addrParseErr, prefixParseErr)
err = fmt.Errorf("Address %s is not correct, error: %v", address, err)
break
}
return err
}
+77
View File
@@ -0,0 +1,77 @@
package netacl
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
)
func TestNetACLComplex(t *testing.T) {
var err error
var enabled bool
al := NewNetACL()
require.NotNil(t, al)
err = al.AddDisabledAddresses("10.0.0.1")
require.NoError(t, err)
err = al.AddDisabledAddresses("192.168.10.0/24")
require.NoError(t, err)
err = al.AddEnabledAddresses("192.168.100.0/16")
require.NoError(t, err)
yamlData, err := yaml.Marshal(al)
require.NoError(t, err)
fmt.Printf("\n%s\n", string(yamlData))
enabled, err = al.AddressIsEnabled("8.7.7.7")
require.NoError(t, err)
require.Equal(t, false, enabled)
enabled, err = al.AddressIsEnabled("192.168.10.0")
require.NoError(t, err)
require.Equal(t, false, enabled)
enabled, err = al.AddressIsEnabled("192.168.100.7")
require.NoError(t, err)
require.Equal(t, true, enabled)
}
func TestNetACLAllEnabled(t *testing.T) {
var err error
var enabled bool
al := NewNetACL()
require.NotNil(t, al)
err = al.AddEnabledAddresses("0.0.0.1/0")
require.NoError(t, err)
err = al.AddEnabledAddresses("10.0.1.1/17")
require.NoError(t, err)
yamlData, err := yaml.Marshal(al)
require.NoError(t, err)
fmt.Printf("\n%s\n", string(yamlData))
enabled, err = al.AddressIsEnabled("192.168.100.7")
require.NoError(t, err)
require.Equal(t, true, enabled)
}
func TestNetACLLoopEnabled(t *testing.T) {
var err error
var enabled bool
al := NewNetACL()
require.NotNil(t, al)
err = al.AddEnabledAddresses("::1/0")
require.NoError(t, err)
yamlData, err := yaml.Marshal(al)
require.NoError(t, err)
fmt.Printf("\n%s\n", string(yamlData))
enabled, err = al.AddressIsEnabled("::1")
require.NoError(t, err)
require.Equal(t, true, enabled)
}
+101
View File
@@ -0,0 +1,101 @@
syntax = "proto3";
option go_package = ".;mbctl";
package mbasecontrol;
service Control {
rpc getHello(getHelloParams) returns (getHelloResult) {};
rpc createAccount(createAccountParams) returns (createAccountResult) {};
rpc deleteAccount(deleteAccountParams) returns (deleteAccountResult) {};
rpc updateAccount(updateAccountParams) returns (updateAccountResult) {};
rpc getAccount(getAccountParams) returns (getAccountResult) {};
rpc listAccounts(listAccountsParams) returns (listAccountsResult) {};
rpc setGrant(setGrantParams) returns (setGrantResult) {};
rpc deleteGrant(deleteGrantParams) returns (deleteGrantResult) {};
rpc getDump(getDumpParams) returns (getDumpResult) {};
rpc restoreDump(restoreDumpParams) returns (restoreDumpResult) {};
}
message getDumpParams {}
message getDumpResult {
string dump = 1;
}
message restoreDumpParams {
string dump = 1;
bool deleteAllRecords = 2;
}
message restoreDumpResult {
}
message setGrantParams {
string username = 1;
int64 accountID = 2;
string operation = 3;
}
message setGrantResult {
int64 grantID = 1;
}
message deleteGrantParams {
string username = 1;
int64 accountID = 2;
string operation = 3;
}
message deleteGrantResult {}
message createAccountParams {
string username = 1;
string password = 2;
}
message createAccountResult {
int64 accountID = 1;
}
message deleteAccountParams {
string username = 1;
int64 accountID = 2;
}
message deleteAccountResult {}
message updateAccountParams {
string username = 1;
int64 accountID = 2;
string newUsername = 3;
string newPassword = 4;
bool disabled = 5;
}
message updateAccountResult {}
message getAccountParams {}
message getAccountResult {}
message listAccountsParams {}
message listAccountsResult {
repeated accountShortDescr accounts = 1;
}
message accountShortDescr {
string username = 1;
bool disabled = 2;
string createdAt = 3;
string updatedAt = 4;
repeated grantShortDescr grants = 5;
}
message grantShortDescr {
string operation = 1;
string createdAt = 2;
}
message getHelloParams {}
message getHelloResult {
string message = 1;
}