Skip to content
Snippets Groups Projects
Commit 089ce7af authored by Sergey Yakubov's avatar Sergey Yakubov
Browse files

Merge pull request #134 in ASAPO/asapo from feature_ASAPO-129-improve-tokens to develop

* commit '0ad1de8d': (23 commits)
  fix tests
  updated authorizer and tests
  broker authorizes via a service
  fix win tests
  fix tests
  fix tests
  update asapo cli and tests
  update receiver authorization
  producer sends token in a separate send
  switch broker to JWT tokens
  return access type in auth response
  use separate secrets for user and admi tokens
  refactor
  switch to jwt admin token
  update tests
  cli to create tokens
  fix test
  fix tests
  fix test
  add admin token, tests
  ...
parents 5175f5bb 0ad1de8d
No related branches found
No related tags found
No related merge requests found
Showing
with 544 additions and 82 deletions
## 21.03.0 (in progress)
IMPROVEMENTS
<<<<<<< HEAD
* Producer API - queue limits in Python, for C++ return original data in error custom data
* Consumer API - add GetCurrentDatasetCount/get_current_dataset_count function with option to include or exclude incomplete datasets
* Consumer API - GetStreamList/get_stream_list - can filter finished/unfinished streams now
* Producer/Consumer API - StreamInfo structure/Python dictionary include more information (is stream finished or not, ...)
* Switch to JWT tokens (token has more symbols, expiration time, can be revoked and there are two type of tokens - with read/write access rights)
BREAKING CHANGES
* Consumer API (C++ only)- GetStreamList has now extra argument StreamFilter
......
......@@ -4,8 +4,6 @@ SOURCE_DIR=$1
OUT_DIR=$2
ASAPO_MINIMUM_COVERAGE=$3
export GOPATH=$GOPATH:$4
echo $OUT_DIR
touch $OUT_DIR/coverage.out
......
......@@ -50,6 +50,8 @@ function(prepare_asapo)
configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/broker_settings.json.tpl broker.json.tpl COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/file_transfer_settings.json.tpl file_transfer.json.tpl COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/auth_secret.key auth_secret.key COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/admin_token.key admin_token.key COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/auth_secret_admin.key auth_secret_admin.key COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/nginx.conf.tpl nginx.conf.tpl COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/config/nomad/nginx.nmd.in nginx.nmd @ONLY)
......
......@@ -13,11 +13,6 @@ function(gotest target source_dir test_source_files)
if (BUILD_TESTS)
add_test(NAME test-${target} COMMAND go test ${test_source_files}
WORKING_DIRECTORY ${source_dir})
set_property(
TEST
test-${target}
PROPERTY
ENVIRONMENT "GOPATH=${gopath}")
message(STATUS "Added test 'test-${target}'")
if (CMAKE_COMPILER_IS_GNUCXX)
add_test(NAME coveragetest-${target}
......@@ -29,16 +24,11 @@ function(gotest target source_dir test_source_files)
endif ()
endfunction()
function(go_integration_test target test_source_files label)
function(go_integration_test target source_dir test_source_files label)
if (BUILD_TESTS)
add_test(NAME test-${target} COMMAND go test ${test_source_files} -run ${label}
-tags integration_tests
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_property(
TEST
test-${target}
PROPERTY
ENVIRONMENT "GOPATH=${gopath}")
WORKING_DIRECTORY ${source_dir})
message(STATUS "Added test 'test-${target}'")
endif ()
endfunction()
set (TARGET_NAME asapo)
if (NOT "$ENV{GOPATH}" STREQUAL "")
set(GOPATH $ENV{GOPATH})
endif()
if (NOT GOPATH)
message (FATAL_ERROR "GOPATH not set")
endif()
message(STATUS "global gopath ${GOPATH}")
IF(WIN32)
set (gopath "${GOPATH}\;${CMAKE_CURRENT_SOURCE_DIR}\;${CMAKE_SOURCE_DIR}/common/go")
set (exe_name "${TARGET_NAME}.exe")
ELSE()
set (gopath ${GOPATH}:${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/common/go)
set (exe_name "${TARGET_NAME}")
# set (GO_OPTS "GOOS=linux;CGO_ENABLED=0")
ENDIF()
......@@ -22,13 +10,13 @@ ENDIF()
include(testing_go)
add_custom_target(asapo ALL
COMMAND ${CMAKE_COMMAND} -E env GOPATH=${gopath}
${GO_OPTS} go build -o ${exe_name} asapo_tools/main
VERBATIM)
COMMAND go build ${GO_OPTS} -o ${CMAKE_CURRENT_BINARY_DIR}/${exe_name} main/asapo.go
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/asapo_tools
VERBATIM)
define_property(TARGET PROPERTY EXENAME
BRIEF_DOCS <executable name>
FULL_DOCS <full-doc>)
set_target_properties(${TARGET_NAME} PROPERTIES EXENAME ${CMAKE_CURRENT_BINARY_DIR}/${exe_name})
gotest(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}" "./...")
gotest(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/src/asapo_tools" "./...")
package cli
import (
"errors"
"flag"
"fmt"
)
......@@ -21,10 +20,6 @@ func (cmd *command) description(d string) bool {
return false
}
func (cmd *command) errBadOptions(err string) error {
return errors.New("asapo " + cmd.name + ": " + err + "\nType 'asapo " + cmd.name + " --help'")
}
// createDefaultFlagset creates new flagset and adds default help behaviour.
func (cmd *command) createDefaultFlagset(description, args string) *flag.FlagSet {
......
......@@ -10,7 +10,7 @@ var CommandTests = []struct {
cmd command
answer string
}{
{command{"token", []string{"-secret", "secret_file", "beamtime"}}, "secret"},
{command{"token", []string{"-secret", "secret_file","-type","read","-endpoint","bla", "beamtime"}}, "secret"},
{command{"dummy", []string{"description"}}, "wrong"},
}
......@@ -22,7 +22,6 @@ func TestCommand(t *testing.T) {
err := DoCommand(test.cmd.name, test.cmd.args)
assert.Contains(t, err.Error(), test.answer, "")
assert.NotNil(t, err, "Should be error")
}
}
......
package cli
import (
"asapo_common/structs"
"asapo_common/utils"
"asapo_tools/rest_client"
"bytes"
"encoding/json"
"errors"
"os"
"fmt"
"asapo_common/utils"
"io"
"net/http"
"os"
)
type tokenFlags struct {
Name string
SecretFile string
Name string
Endpoint string
AccessType string
SecretFile string
TokenDetails bool
}
func generateToken(id string,secret string) string {
hmac := utils.NewHMACAuth(secret)
token,err := hmac.GenerateToken(&id)
func generateToken(flags tokenFlags, secret string) string {
// hmac := utils.NewHMACAuth(secret)
// token,err := hmac.GenerateToken(&id)
if (err!=nil) {
fmt.Println(err.Error())
}
return token
// if (err!=nil) {
// fmt.Println(err.Error())
// }
// return token
return ""
}
// GenerateToken generates token for consumers
// CommandToken receives token from authorization server
func (cmd *command) CommandToken() error {
message_string := "Generate token"
......@@ -38,21 +47,62 @@ func (cmd *command) CommandToken() error {
}
secret, err := utils.ReadFirstStringFromFile(flags.SecretFile)
if err !=nil {
if err != nil {
return err
}
fmt.Fprintf(outBuf, "%s\n", generateToken(flags.Name,secret))
request := structs.IssueTokenRequest{
Subject: map[string]string{"beamtimeId": flags.Name},
DaysValid: 180,
AccessType: flags.AccessType,
}
json_data, _ := json.Marshal(request)
path := flags.Endpoint + "/admin/issue"
return nil
}
req, err := http.NewRequest("POST", path, bytes.NewBuffer(json_data))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer "+secret)
resp, err := rest_client.Client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New("returned " + resp.Status + ": " + string(body))
}
if flags.TokenDetails {
fmt.Fprintf(outBuf, "%s\n", string(body))
return nil
}
var token structs.IssueTokenResponse
err = json.Unmarshal(body, &token)
if err == nil {
fmt.Fprintf(outBuf, "%s\n", token.Token)
}
return err
}
func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) {
var flags tokenFlags
flagset := cmd.createDefaultFlagset(message_string, "<token_body>")
flagset.StringVar(&flags.SecretFile, "secret", "", "path to file with secret")
flagset.StringVar(&flags.AccessType, "type", "", "access type")
flagset.StringVar(&flags.Endpoint, "endpoint", "", "asapo endpoint")
flagset.BoolVar(&flags.TokenDetails, "token-details", false, "output token details")
flagset.Parse(cmd.args)
......@@ -63,13 +113,20 @@ func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) {
flags.Name = flagset.Arg(0)
if flags.Name == "" {
return flags, errors.New("beamtime id or beamline missed ")
return flags, errors.New("payload missed ")
}
if flags.SecretFile == "" {
return flags, errors.New("secret file missed ")
}
if flags.Endpoint == "" {
return flags, errors.New("endpoint missed ")
}
if flags.AccessType != "read" && flags.AccessType != "write" {
return flags, errors.New("incorrect or missed token access type ")
}
return flags, nil
......
package cli
import (
"asapo_tools/mocks"
"asapo_tools/rest_client"
"encoding/json"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"bytes"
"github.com/stretchr/testify/assert"
"io/ioutil"
"os"
)
var tokenTests = []struct {
cmd command
answer string
withDetails bool
ok bool
msg string
}{
{command{args: []string{"beamtime_id"}}, "secret", "no secret parameter"},
{command{args: []string{"-secret","secret.tmp"}}, "beamtime id", "no file"},
{command{args: []string{"-secret","not_existing_file","beamtime_id"}}, "not_existing_file", "no file"},
{command{args: []string{"-secret","secret.tmp","beamtime_id"}}, "eodk3s5ZXwACLGyVA63MZYcOTWuWE4bceI9Vxl9zejI=", "no file"},
{command{args: []string{"beamtime_id"}}, false,false, "no secret parameter"},
{command{args: []string{"-secret","secret.tmp"}}, false,false, "no file"},
{command{args: []string{"-secret","not_existing_file","payload"}}, false, false, "no file"},
{command{args: []string{"-secret","secret.tmp","beamtime_id"}},false, false, "type is missing"},
{command{args: []string{"-secret","secret.tmp","-type","read","beamtime_id"}}, false, false, "endpoint is missing"},
{command{args: []string{"-secret","secret.tmp","-type","read","-endpoint","endpoint","-token-details","beamtime_id"}},true, true, "ok"},
{command{args: []string{"-secret","secret.tmp","-type","read","-endpoint","endpoint","beamtime_id"}}, false,true, "without details"},
}
func TestParseTokenFlags(t *testing.T) {
ioutil.WriteFile("secret.tmp", []byte("secret"), 0644)
outBuf = new(bytes.Buffer)
rest_client.Client = &mocks.MockClient{}
mocks.DoFunc = func(req *http.Request) (*http.Response, error) {
json := `{"Token":"blabla","Uri":"`+req.URL.Path+`"}`
r := ioutil.NopCloser(bytes.NewReader([]byte(json)))
return &http.Response{
StatusCode: 200,
Body: r,
}, nil
}
for _, test := range tokenTests {
outBuf = new(bytes.Buffer)
err := test.cmd.CommandToken()
if err == nil {
assert.Contains(t, outBuf.(*bytes.Buffer).String(), test.answer, test.msg)
if test.ok {
assert.Nil(t, err, test.msg)
resp := struct {
Token string
Uri string
}{}
if test.withDetails {
err := json.Unmarshal(outBuf.(*bytes.Buffer).Bytes(),&resp)
assert.Nil(t, err, test.msg)
assert.Equal(t, "blabla", resp.Token,test.msg)
assert.Equal(t, "endpoint/admin/issue",resp.Uri, test.msg)
} else {
assert.Equal(t, "blabla\n", outBuf.(*bytes.Buffer).String(),test.msg)
}
} else {
assert.Contains(t, err.Error(), test.answer, test.msg)
assert.NotNil(t, err, test.msg)
}
}
......
module asapo_tools
go 1.16
replace asapo_common v0.0.0 => ../../../common/go/src/asapo_common
require (
asapo_common v0.0.0
github.com/kr/pretty v0.2.0 // indirect
github.com/stretchr/testify v1.7.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
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/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package main
import (
"asapo_tools/rest_client"
"flag"
"fmt"
"net/http"
"os"
"asapo_common/version"
"asapo_tools/cli"
......@@ -20,6 +22,8 @@ func main() {
flag.Parse()
rest_client.Client = &http.Client{}
if *flHelp || flag.NArg() == 0 {
flag.Usage()
cli.PrintAllCommands()
......
package mocks
import "net/http"
type MockClient struct {
DoFunc func(req *http.Request) (*http.Response, error)
}
func (m *MockClient) Do(req *http.Request) (*http.Response, error) {
return DoFunc(req)
}
var DoFunc func(req *http.Request) (*http.Response, error)
package rest_client
import "net/http"
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
var Client HTTPClient
set (TARGET_NAME asapo-authorizer)
if (NOT "$ENV{GOPATH}" STREQUAL "")
set(GOPATH $ENV{GOPATH})
endif()
if (NOT GOPATH)
message (FATAL_ERROR "GOPATH not set")
endif()
message(STATUS "global gopath ${GOPATH}")
IF(WIN32)
set (gopath "${GOPATH}\;${CMAKE_CURRENT_SOURCE_DIR}\;${CMAKE_SOURCE_DIR}/common/go")
set (exe_name "${TARGET_NAME}.exe")
ELSE()
set (gopath ${GOPATH}:${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/common/go)
set (exe_name "${TARGET_NAME}")
ENDIF()
......@@ -23,8 +11,8 @@ include(testing_go)
configure_file(docker/Dockerfile . COPYONLY)
add_custom_target(asapo-authorizer ALL
COMMAND ${CMAKE_COMMAND} -E env GOPATH=${gopath}
go build ${GO_OPTS} -o ${exe_name} asapo_authorizer/main
COMMAND go build ${GO_OPTS} -o ${CMAKE_CURRENT_BINARY_DIR}/${exe_name} main/authorizer.go
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/asapo_authorizer
VERBATIM)
define_property(TARGET PROPERTY EXENAME
BRIEF_DOCS <executable name>
......@@ -32,6 +20,4 @@ define_property(TARGET PROPERTY EXENAME
set_target_properties(asapo-authorizer PROPERTIES EXENAME ${CMAKE_CURRENT_BINARY_DIR}/${exe_name})
gotest(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}" "./...")
#go_integration_test(${TARGET_NAME}-connectdb "./..." "MongoDBConnect")
#go_integration_test(${TARGET_NAME}-nextrecord "./..." "MongoDBNext")
gotest(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/src/asapo_authorizer" "./...")
package authorization
import (
"asapo_common/structs"
"asapo_common/utils"
"encoding/json"
"github.com/rs/xid"
"time"
)
type Auth struct {
authUser utils.Auth
authAdmin utils.Auth
authJWT utils.Auth
}
func NewAuth(authUser,authAdmin,authJWT utils.Auth) *Auth {
return &Auth{authUser,authAdmin,authJWT}
}
func (auth *Auth) AdminAuth() utils.Auth {
return auth.authAdmin
}
func (auth *Auth) UserAuth() utils.Auth {
return auth.authUser
}
func (auth *Auth) JWTAuth() utils.Auth {
return auth.authJWT
}
func subjectFromRequest(request structs.IssueTokenRequest) string {
for key,value := range request.Subject {
switch key {
case "beamline":
return utils.SubjectFromBeamline(value)
case "beamtimeId":
return utils.SubjectFromBeamtime(value)
default:
return value
}
}
return ""
}
func (auth *Auth) PrepareAccessToken(request structs.IssueTokenRequest, userToken bool) (string, error) {
var claims utils.CustomClaims
var extraClaim structs.AccessTokenExtraClaim
claims.Subject = subjectFromRequest(request)
extraClaim.AccessType = request.AccessType
claims.ExtraClaims = &extraClaim
claims.SetExpiration(time.Duration(request.DaysValid*24) * time.Hour)
uid := xid.New()
claims.Id = uid.String()
if userToken {
return auth.UserAuth().GenerateToken(&claims)
} else {
return auth.AdminAuth().GenerateToken(&claims)
}
}
func UserTokenResponce(request structs.IssueTokenRequest, token string) []byte {
expires := ""
if request.DaysValid>0 {
expires = time.Now().Add(time.Duration(request.DaysValid*24) * time.Hour).UTC().Format(time.RFC3339)
}
answer := structs.IssueTokenResponse{
Token: token,
AccessType: request.AccessType,
Sub: subjectFromRequest(request),
Expires: expires,
}
res, _ := json.Marshal(answer)
return res
}
// Package contains asapo commands that can be executed from command line.
// Every CommandXxxx function that is a member of a cmd struct processes asapo xxxx command
package cli
import (
"errors"
"flag"
"fmt"
"io"
"os"
"reflect"
"strings"
)
var flHelp bool
var outBuf io.Writer = os.Stdout
func printHelp(f *flag.FlagSet) bool {
if flHelp {
f.Usage()
return true
} else {
return false
}
}
// DoCommand takes command name as a parameter and executes corresponding to this name cmd method
func DoCommand(name string, args []string) error {
commandName := "Command" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
cmd := new(command)
commandName = strings.ReplaceAll(commandName,"-","_")
methodVal := reflect.ValueOf(cmd).MethodByName(commandName)
if !methodVal.IsValid() {
return errors.New("wrong "+ProgramName+" command: " + name + "\nType '"+os.Args[0]+" -help'")
}
cmd.name = strings.ReplaceAll(name,"-","_")
cmd.args = args
method := methodVal.Interface().(func() error)
return method()
}
// PrintAllCommands prints all available commands (found wihtin methods of cmd)
func PrintAllCommands() {
fmt.Fprintln(outBuf, "\nCommands:")
cmd := new(command)
CmdType := reflect.TypeOf(cmd)
for i := 0; i < CmdType.NumMethod(); i++ {
methodVal := CmdType.Method(i)
if strings.HasPrefix(methodVal.Name, "Command") {
method := methodVal.Func.Interface().(func(*command) error)
cmd.name = strings.ToLower(methodVal.Name)[7:]
cmd.name = strings.ReplaceAll(cmd.name,"_","-")
cmd.args = []string{"description"}
method(cmd)
}
}
}
package cli
import (
"errors"
"flag"
"fmt"
"os"
)
var ProgramName string
// A command consists of a command name and arguments, passed to this command (all after program name ...)
type command struct {
name string
args []string
}
// description prints description line and returns true if first command argument is "description".
func (cmd *command) description(d string) bool {
if len(cmd.args) == 1 && cmd.args[0] == "description" {
fmt.Fprintf(outBuf, " %-10s %s\n", cmd.name, d)
return true
}
return false
}
func (cmd *command) errBadOptions(err string) error {
return errors.New(ProgramName + " " + cmd.name + ": " + err + "\nType '" +os.Args[0] +" " + cmd.name + " -help'")
}
// createDefaultFlagset creates new flagset and adds default help behaviour.
func (cmd *command) createDefaultFlagset(description, args string) *flag.FlagSet {
flags := flag.NewFlagSet(cmd.name, flag.ContinueOnError)
flags.BoolVar(&flHelp, "help", false, "Print usage")
flags.Usage = func() {
fmt.Fprintf(outBuf, "Usage:\t\n"+ProgramName+" %s "+args, cmd.name)
fmt.Fprintf(outBuf, "\n\n%s\n", description)
flags.PrintDefaults()
}
return flags
}
package cli
import (
"asapo_authorizer/authorization"
"asapo_authorizer/server"
"asapo_common/utils"
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
var CommandTests = []struct {
cmd command
ok bool
msg string
}{
{command{"create-token", []string{"-type", "user-token", "-beamtime","123","-access-type","read","-duration-days","1"}}, true,"ok"},
{command{"dummy", []string{"description"}}, false,"wrong command"},
}
func TestCommand(t *testing.T) {
outBuf = new(bytes.Buffer)
server.Auth = authorization.NewAuth(utils.NewJWTAuth("secret"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret"))
for _, test := range CommandTests {
outBuf.(*bytes.Buffer).Reset()
err := DoCommand(test.cmd.name, test.cmd.args)
if !test.ok {
assert.NotNil(t, err, "Should be error",test.msg)
} else {
assert.Nil(t, err, "Should be ok",test.msg)
}
}
}
func TestPrintAllCommands(t *testing.T) {
outBuf = new(bytes.Buffer)
PrintAllCommands()
assert.Contains(t, outBuf.(*bytes.Buffer).String(), "token", "all commands must have token")
}
package cli
import (
"asapo_authorizer/authorization"
"asapo_authorizer/server"
"asapo_common/structs"
"errors"
"fmt"
"os"
)
type tokenFlags struct {
Type string
AccessType string
Beamtime string
Beamline string
DaysValid int
}
func userTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err error) {
if (flags.Beamline=="" && flags.Beamtime=="") || (flags.Beamline!="" && flags.Beamtime!="") {
return request,errors.New("beamtime or beamline must be set")
}
if flags.AccessType!="read" && flags.AccessType!="write" {
return request,errors.New("access type must be read of write")
}
request.Subject = make(map[string]string,1)
if (flags.Beamline!="") {
request.Subject["beamline"]=flags.Beamline
} else {
request.Subject["beamtimeId"]=flags.Beamtime
}
request.AccessType = flags.AccessType
request.DaysValid = flags.DaysValid
return
}
func adminTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err error) {
if flags.Beamline+flags.Beamtime!="" {
return request,errors.New("beamtime and beamline must not be set for admin token")
}
if flags.AccessType!="create" && flags.AccessType!="revoke" && flags.AccessType!="list" {
return request,errors.New("access type must be create,revoke of list")
}
request.Subject = make(map[string]string,1)
request.Subject["user"]="admin"
request.AccessType = flags.AccessType
request.DaysValid = flags.DaysValid
return
}
func (cmd *command) CommandCreate_token() (err error) {
message_string := "Generate token"
if cmd.description(message_string) {
return nil
}
flags, err := cmd.parseTokenFlags(message_string)
if err != nil {
return err
}
request, userToken, err := getTokenRequest(flags)
if err != nil {
return err
}
token, err := server.Auth.PrepareAccessToken(request,userToken)
if err != nil {
return err
}
answer := authorization.UserTokenResponce(request, token)
fmt.Fprintf(outBuf, "%s\n", string(answer))
return nil
}
func getTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, userToken bool, err error) {
switch flags.Type {
case "user-token":
request, err = userTokenRequest(flags)
userToken = true
case "admin-token":
request, err = adminTokenRequest(flags)
userToken = false
default:
return structs.IssueTokenRequest{}, false, errors.New("wrong token type")
}
if err != nil {
return structs.IssueTokenRequest{},false, err
}
return request, userToken, err
}
func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) {
var flags tokenFlags
flagset := cmd.createDefaultFlagset(message_string, "")
flagset.StringVar(&flags.Type, "type", "", "token type")
flagset.StringVar(&flags.Beamtime, "beamtime", "", "beamtime for user token")
flagset.StringVar(&flags.Beamline, "beamline", "", "beamline for user token")
flagset.StringVar(&flags.AccessType, "access-type", "", "read/write for user token")
flagset.IntVar(&flags.DaysValid, "duration-days", 0, "token duration (in days)")
flagset.Parse(cmd.args)
if printHelp(flagset) {
os.Exit(0)
}
if flags.Type == "" {
return flags, errors.New("secret file missed ")
}
return flags, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment