grim/hgkeeper
Clone
Summary
Browse
Changes
Graph
auto reload the access model and policy if the admin repo is accessed. This does both push and pulls right now.
2019-09-12, Gary Kramlich
e2f2ff46f491
auto reload the access model and policy if the admin repo is accessed. This does both push and pulls right now.
//go:generate esc -o resources.go -pkg setup -include resources\/.+ -prefix resources/ .
package
setup
import
(
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"text/template"
"golang.org/x/crypto/ssh"
"bitbucket.org/rw_grim/hgkeeper/globals"
"bitbucket.org/rw_grim/hgkeeper/hg"
)
type
Command
struct
{
AdminUsername
string
`kong:"flag,name='admin-username',help='the name of the initial admin user',required='true'"`
AdminSSHPubkey
string
`kong:"flag,name='admin-pubkey',help='the path to the ssh pubkey to use for the admin',required='true',type='existingfile'"`
}
var
(
hgUsername
=
"hgkeeper"
commitMessage
=
"initial revision"
)
func
runCmd
(
cmd
*
hg
.
Command
)
error
{
output
,
err
:=
cmd
.
Cmd
().
CombinedOutput
()
if
len
(
output
)
>
0
{
fmt
.
Printf
(
"%s\n"
,
output
)
}
return
err
}
func
(
c
*
Command
)
createAdminRepo
(
reposPath
,
adminRepo
string
)
error
{
path
:=
filepath
.
Join
(
reposPath
,
adminRepo
)
// create the admin repo
if
err
:=
runCmd
(
hg
.
Init
(
path
));
err
!=
nil
{
return
err
}
filenames
:=
[]
string
{}
// walk through the embeded resources and dump them into the directory
for
name
,
data
:=
range
_escData
{
// skip directories as we only add the ones were we have files
if
data
.
isDir
{
continue
}
// we're copying a regular file now, so figure out the paths so we can
// create them if necessary. We have to special case dothg because
// using .hg causes issues
var
absname
string
if
strings
.
HasPrefix
(
name
,
"/dothg/"
)
{
absname
=
filepath
.
Join
(
path
,
".hg"
,
name
[
7
:])
}
else
{
absname
=
filepath
.
Join
(
path
,
name
)
}
dirname
:=
filepath
.
Dir
(
absname
)
// if we don't have the directory create it
if
_
,
err
:=
os
.
Stat
(
dirname
);
err
!=
nil
{
if
os
.
IsNotExist
(
err
)
{
if
err
:=
os
.
MkdirAll
(
dirname
,
0755
);
err
!=
nil
{
return
err
}
}
else
{
return
err
}
}
// If this is a template file, execute it
if
filepath
.
Ext
(
name
)
==
".template"
{
absname
=
absname
[:
len
(
absname
)
-
len
(
".template"
)]
tplate
,
err
:=
template
.
New
(
name
).
Parse
(
FSMustString
(
false
,
name
))
if
err
!=
nil
{
return
err
}
file
,
err
:=
os
.
Create
(
absname
)
if
err
!=
nil
{
return
err
}
defer
file
.
Close
()
tplateData
:=
struct
{
AdminRepo
string
AdminUsername
string
}{
AdminRepo
:
"/"
+
adminRepo
,
AdminUsername
:
c
.
AdminUsername
,
}
if
err
:=
tplate
.
Execute
(
file
,
tplateData
);
err
!=
nil
{
fmt
.
Printf
(
"error: %v\n"
,
err
)
return
err
}
}
else
{
// If it's not a template just write it out
if
err
:=
ioutil
.
WriteFile
(
absname
,
FSMustByte
(
false
,
name
),
0644
);
err
!=
nil
{
return
err
}
}
rel
,
err
:=
filepath
.
Rel
(
path
,
absname
)
if
err
!=
nil
{
return
err
}
if
!
strings
.
HasPrefix
(
rel
,
fmt
.
Sprintf
(
".hg%c"
,
filepath
.
Separator
))
{
filenames
=
append
(
filenames
,
rel
)
}
}
// create our keys directory
keysDir
:=
filepath
.
Join
(
path
,
"keys"
)
if
err
:=
os
.
MkdirAll
(
keysDir
,
0755
);
err
!=
nil
{
return
err
}
// now copy the admin key into the keys directory
pubkey
,
err
:=
ioutil
.
ReadFile
(
c
.
AdminSSHPubkey
)
if
err
!=
nil
{
return
err
}
adminPubkey
:=
filepath
.
Join
(
keysDir
,
c
.
AdminUsername
)
relAdminPubkey
,
err
:=
filepath
.
Rel
(
path
,
adminPubkey
)
if
err
!=
nil
{
return
err
}
filenames
=
append
(
filenames
,
relAdminPubkey
)
if
err
:=
ioutil
.
WriteFile
(
adminPubkey
,
pubkey
,
0644
);
err
!=
nil
{
return
err
}
// add our files
if
err
:=
runCmd
(
hg
.
Add
(
path
,
filenames
...
));
err
!=
nil
{
return
err
}
// commit our changes
if
err
:=
runCmd
(
hg
.
Commit
(
path
,
hgUsername
,
commitMessage
));
err
!=
nil
{
return
err
}
return
nil
}
func
isPubkey
(
filename
string
)
error
{
bytes
,
err
:=
ioutil
.
ReadFile
(
filename
)
if
err
!=
nil
{
return
err
}
// ssh.ParsePublicKey wants the wire format which includes a size
// parameter. However, since every entry of an authorized_key file is
// just a straight public key, we can use ParseAuthorizedKey to check if
// the file is a public key.
if
_
,
_
,
_
,
_
,
err
:=
ssh
.
ParseAuthorizedKey
(
bytes
);
err
!=
nil
{
return
err
}
return
nil
}
func
(
c
*
Command
)
Run
(
g
*
globals
.
Globals
)
error
{
// make sure the adminPubkey is an SSH pubkey as it is too easy to
// accidentally give the path to the private key rather than the public
// key, and we do no want the private key in the repository.
if
err
:=
isPubkey
(
c
.
AdminSSHPubkey
);
err
!=
nil
{
return
fmt
.
Errorf
(
"%s is not a public key file"
,
c
.
AdminSSHPubkey
)
}
// create the admin repo
if
err
:=
c
.
createAdminRepo
(
g
.
ReposPath
,
g
.
AdminRepo
);
err
!=
nil
{
// do clean up
return
err
}
return
nil
}