How can I authenticate with Artifactory in Go? - go

I cannot really make any sense of the extremely terse documentation - my use case is I have a file at a known URL in Artifactory and I want to download it. For this I need to authenticate with Artifactory.
I have this code:
func authenticateToArtifactory() string {
rtDetails := auth.NewArtifactoryDetails()
rtDetails.SetUrl(artifactoryURL)
fmt.Printf("Artifactory login name....\n")
var userName string
fmt.Scanln(&userName)
fmt.Printf("Artifactory password....\n")
var passWord string
fmt.Scanln(&passWord)
rtDetails.SetUser(userName)
rtDetails.SetPassword(passWord)
apiKey := rtDetails.GetAPIKey()
fmt.Printf("API key is %s\n", apiKey)
return apiKey
}
But it doesn't work - to be honest this isn't a surprise as there isn't even a getAPIKey() call defined - but I really don't know what to do here.
What's the right way to do this?
Edit: From reading around I think I might have to use a http client for this - ie I cannot authenticate with Artifactory directly: is that correct?

After a lot of frustration and trial and error, I think I have this working - the documentation is shockingly poor, so I do hope this helps someone else.
Here's a basic Set up... (getting all these trailing '/' in is essential and as far as I can see none of this is documented):
var artifactoryURL = "https://your.remote.server/artifactory/"
rtDetails := auth.NewArtifactoryDetails()
rtDetails.SetUrl(artifactoryURL)
fmt.Printf("Artifactory login name....\n")
var userName string
userName = [some way of getting user name eg via command line]
fmt.Printf("Artifactory password....\n")
var passWord string
passWord = [some way of getting artifactory password eg via command line]
rtDetails.SetUser(userName)
rtDetails.SetPassword(passWord)
artifactoryHTTP := &http.Client{Transport: &http.Transport{}}
serviceConfig, err := config.NewConfigBuilder().SetServiceDetails(rtDetails).SetDryRun(false).SetHttpClient(artifactoryHTTP).Build()
if err != nil {
panic(err)
}
rtManager, errSC := artifactory.New(serviceConfig)
if errSC != nil {
panic(errSC)
}
params := services.NewDownloadParams()
params.Pattern = "[repo_name]/path/to/your/file/your_file"
_, _, err = rtManager.DownloadFiles(params)
if err != nil {
fmt.Printf("%s\n", err.Error())
panic(err)
}
The repo name is the first bit of the file's URL after artifactory: eg
http://my.server.address/artifactory/repo_name/blah/blah/blah/file_I_want.tar.gz

Related

Getting Access token is empty error while running msgraph-sdk-go training example code

While trying to run the msgraph-sdk-go training code from here: https://github.com/microsoftgraph/msgraph-training-go, I'm getting InvalidAuthenticationTokenmsg: Access token is empty while executing the Graph API calls.
I configured a Microsoft developer account with instant sandbox for trial purpose.
I created an app registration as mentioned in the tutorial here and granted required permissions for the app.
The code is able to get the AppToken, but for calls to get Users, it fails with the above error. Am I missing something here?
I tried below code from the example for msgraph-training
func (g *GraphHelper) InitializeGraphForAppAuth() error {
clientId := os.Getenv("CLIENT_ID")
tenantId := os.Getenv("TENANT_ID")
clientSecret := os.Getenv("CLIENT_SECRET")
credential, err := azidentity.NewClientSecretCredential(tenantId, clientId, clientSecret, nil)
if err != nil {
return err
}
g.clientSecretCredential = credential
// Create an auth provider using the credential
authProvider, err := auth.NewAzureIdentityAuthenticationProviderWithScopes(g.clientSecretCredential, []string{
"https://graph.microsoft.com/.default",
})
if err != nil {
return err
}
// Create a request adapter using the auth provider
adapter, err := msgraphsdk.NewGraphRequestAdapter(authProvider)
if err != nil {
return err
}
// Create a Graph client using request adapter
client := msgraphsdk.NewGraphServiceClient(adapter)
g.appClient = client
return nil
}
// This part works, and I get the AppToken with required scope, once decoded.
func (g *GraphHelper) GetAppToken() (*string, error) {
token, err := g.clientSecretCredential.GetToken(context.Background(), policy.TokenRequestOptions{
Scopes: []string{
"https://graph.microsoft.com/.default",
},
})
if err != nil {
return nil, err
}
fmt.Println("expires on : ", token.ExpiresOn)
return &token.Token, nil
}
// The GetUsers function errors out
func (g *GraphHelper) GetUsers() (models.UserCollectionResponseable, error) {
var topValue int32 = 25
query := users.UsersRequestBuilderGetQueryParameters{
// Only request specific properties
Select: []string{"displayName", "id", "mail"},
// Get at most 25 results
Top: &topValue,
// Sort by display name
Orderby: []string{"displayName"},
}
resp, err := g.appClient.Users().
Get(context.Background(),
&users.UsersRequestBuilderGetRequestConfiguration{
QueryParameters: &query,
})
if err != nil {
fmt.Println("Users.Get got Error", err.Error(), resp)
printOdataError(err)
}
resp, err = g.appClient.Users().
Get(context.Background(),
nil)
if err != nil {
fmt.Println("Users.Get got Error with nil", err.Error(), resp)
}
return resp, err
}
I have added the User.Read.All permission in the app as mentioned in the tutorial.
Instead of getting the list of users, I'm getting below error:
Users.Get got Error error status code received from the API <nil>
error: error status code received from the API
code: InvalidAuthenticationTokenmsg: Access token is empty.Users.Get got Error with nil error status code received from the API <nil>
As you are using client Credential Flow ,you can verify your permission in azure portal , if you have User.Read.All delegated permission , Removes the delegated ones and add the corresponding application ones and don't forget to click on grant administrator consent after that. It should then work.
Hope this helps
Thanks.
Okay, so the fix that did work for me after trial and error was a version mismatch in the example and the actual application I was trying out.
The version of the beta msgraph application I was using was v0.49, whereas the msgraphsdk tutorial was using v0.48. The go mod command picked up the latest v0.49 initially I guess, I updated the go.mod file to use v0.48 after looking at the go.mod file from msgraph-training repository and things started working.
Hope this helps someone else later on.

Gorrila session filesystemstore cannot find session file

I'm starting to build a regular web app with golang and Angular2, and most importantly I'm trying to secure my login with the help of auth0.com. I download the quickstart code from here and try to run the code, it worked for a while and then next time I run it, the /tmp/session file cannot be found any more.
Here are some basic idea of the code auth0.com provides.
1. Initialize the gorilla sessions filesystemstore
2. Then start the authentification process
code is provided below
app.go
var (
Store *sessions.FilesystemStore
)
func Init() error {
Store = sessions.NewFilesystemStore("", []byte("something-very-secret"))
gob.Register(map[string]interface{}{})
return nil
}
login.go
func LoginHandler(w http.ResponseWriter, r *http.Request) {
domain := os.Getenv("AUTH0_DOMAIN")
aud := os.Getenv("AUTH0_AUDIENCE")
conf := &oauth2.Config{
ClientID: os.Getenv("AUTH0_CLIENT_ID"),
ClientSecret: os.Getenv("AUTH0_CLIENT_SECRET"),
RedirectURL: os.Getenv("AUTH0_CALLBACK_URL"),
Scopes: []string{"openid", "profile"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://" + domain + "/authorize",
TokenURL: "https://" + domain + "/oauth/token",
},
}
if aud == "" {
aud = "https://" + domain + "/userinfo"
}
// Generate random state
b := make([]byte, 32)
rand.Read(b)
state := base64.StdEncoding.EncodeToString(b)
session, err := app.Store.Get(r, "state")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Values["state"] = state
err = session.Save(r, w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
audience := oauth2.SetAuthURLParam("audience", aud)
url := conf.AuthCodeURL(state, audience)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
The Error log is
open /tmp/session_46CNLLHBKD... : no such file or directory
I try to understand the code and findout that error log comes from login.go line 39(session, err := app.Store.Get(r, "state")). And I started to track down the code and find out.
login.go:39 -->store.go: 180-->session.go:132-->store.go:186-->store.go:272
you can find store.go and session.go here.
The error log comes from this line: fdata, err := ioutil.ReadFile(filename)
Through the whole process I have not found any function call to save the session file.
I don't understand this error and I don't know why I can run the code at the very beginning, please help me with this problem.
Your any suggestion will be greatly appreciated.Thanks a lot.
I figure out the answer myself
It turns out that I changed my secret key while initializing the gorilla session filesystemstore, but I have not deleted my cookie file in chrome, so it cannot find the tmp sesiion file needed.
I change the key, then delete the coorsponding cookie file and everything is ok now.

Validating GitHub Webhook HMAC signature in Go

I've written the following function for validating the X-Hub-Signature request header returned by the GitHub API as part of the webhook's payload.
func isValidSignature(r *http.Request, key string) bool {
// Assuming a non-empty header
gotHash := strings.SplitN(r.Header.Get("X-Hub-Signature"), "=", 2)
if gotHash[0] != "sha1" {
return false
}
defer r.Body.Close()
b, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Printf("Cannot read the request body: %s\n", err)
return false
}
hash := hmac.New(sha1.New, []byte(key))
if _, err := hash.Write(b); err != nil {
log.Printf("Cannot compute the HMAC for request: %s\n", err)
return false
}
expectedHash := hex.EncodeToString(hash.Sum(nil))
log.Println("EXPECTED HASH:", expectedHash)
return gotHash[1] == expectedHash
}
However, this doesn't seem to work as I'm not able to validate with the correct secret. Here is an example output, if that helps:
HUB SIGNATURE: sha1=026b77d2284bb95aa647736c42f32ea821d6894d
EXPECTED HASH: 86b6fa48bf7643494dc3a8459a8af70008f6881a
I've used the logic from hmac-examples repo as a guideline and implemented the code. However, I am unable to understand the reason behind this discrepancy.
I would be grateful if someone can point out the trivial mistake I'm making here.
Refer: Delivery Headers
This is really embarrasing but still I would like to share how I was able to fix it.
I sent in the wrong key as the input which was causing all the confusion.
Lessons learnt:
The above code snippet is absolutely correct and can be used as a validator.
Every one makes stupid mistakes but only the wise own up to them and rectify.

How to get permissions in local machine for Google Cloud apis (firestore)

I am new to fireStore and trying to retrieve document values but it isn't working as I would expect. I am getting a "permission denied" when trying to access through my IDE and golang.
Code:
func TestConnectToCollection(t *testing.T) {
ctx := context.Background()
client, err := firestore.NewClient(ctx, "<my-Project-ID>")
if err != nil {
// TODO: Handle error.
}
defer client.Close()
doc := client.Doc("profile/test3")
fmt.Println(doc)
fmt.Println(doc.ID)
iter := client.Collection("profile").Documents(ctx)
for {
doc, error := iter.Next()
if error == iterator.Done {
break
}
if error != nil {
fmt.Println(error.Error())
} else {
fmt.Println(doc.Data())
}
}
}
Output:
&{0xc0001725a0 projects/<project-id>/databases/(default)/documents/profile/test3 test3}
test3
rpc error: code = PermissionDenied desc = Missing or insufficient permissions.
rpc error: code = PermissionDenied desc = Missing or insufficient permissions.
rpc error: code = PermissionDenied desc = Missing or insufficient permissions.
rpc error: code = PermissionDenied desc = Missing or insufficient permissions.
Firestore rules - I opened up my firestore rules thinking that was the issue.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
I was able to get this work by changing the following configuration.
Go to Project Overview -> Project Settings -> Service accounts -> "Firebase Admin SDK" -> "Generate new private key" -> "Generate"
Then you see a popup like below
It took me an hour to figure this out. I hope this saves all your time!
I had the same problem, It's sad documentation only provides the most difficult ways to do it, as beginner it's important to make as easy as posible at first, but anyway these are the easiest steps to do it:
If you already have log in the console, jump to step 5
Intall gcloud
Run on your command line gcloud auth login
Login with the google account that owns the project
In your code, your configuration client should be like this:
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectID)
if err != nil {
log.Fatal(err)
}
Run on your command line gcloud beta auth application-default login
And that's it! it's like magic, I had too much headache trying the other ways.
This works for cloud storage too and any google cloud api I'll guess
About your code:
client.Doc("profile/test3")
You are not getting any document at all there, it's just creat a *DocumentRef, in order to get the document you should do:
ctx:=context.Background()
snapShot,err:=client.Doc("profile/test3").Get(ctx)
if err!=nil{
log.Println(err)
return err
}
var myStruct myStructType
if err=snapShot.DataTo(&myStruct);err!=nil{
log.Println(err)
return err
}
Read documentation for more info
The docs tell me that Doc() returns a *DocumentRef, a reference to the document. To fetch the document itself, you should call Get() on it, which gives you a *DocumentSnapshot, and finally call Data() on that.
ref := client.Doc("profile/test3")
snap, err := ref.Get(ctx)
// FIXME error checking
data := snap.Data()

How to parse/create a dynamic url in golang

I am trying to create a buildURL function that can generate an url. However I have run into a problem when facing a dynamic url.
type ShopifyDownloader struct {
Domain string
AccessToken string
}
func (sd *ShopifyDownloader) BuildURL(Path string, Query map[string]string) (string, error) {
u, err := url.Parse("https://123.myshopify.com/admin/orders/count.json?access_token=123")
if err != nil {
fmt.Println("Cannot parse", err)
return "", err
}
u.Host = sd.Domain
u.Path = Path
params := url.Values{}
for key, value := range Query {
params.Add(key, value)
}
u.RawQuery = params.Encode()
nu := u.String()
var buffer bytes.Buffer
buffer.WriteString(nu)
buffer.WriteString("?access_token=")
buffer.WriteString(sd.AccessToken)
bu := buffer.String()
return bu, err
}
It takes domain, token, path and queries and generate an url in string. However it does not work with a dynamic path (with id) like
this /admin/orders/#{id}.json or this /admin/products/#{id}/images/#{id}.json
Does anybody have any suggestion?
Why not just build a url with something simpler at the point where you use it?
url := fmt.Sprintf("%s/admin/orders/count.json?access_token=%s&custom=%s",sd.Domain,sd.Token,customparam)
Then use it. This has the virtue of being completely clear as to intent and the url used in each case while sharing the important stuff (shopify domain etc). Then for your question about ids it becomes easier:
url := fmt.Sprintf("%s/admin/orders/%d.json?access_token=%s",sd.Domain,orderID,sd.Token)
and
url := fmt.Sprintf("%s/admin/products/%d/images/%d.json?access_token=%s",sd.Domain,productID,imageID,sd.Token)
if you feel your params are super complex (for me this would have to be at least 5), just put them in an url.Values (including the token):
params := url.Values{"myparam":{"123"}, "token": {sd.Token}}
url := fmt.Sprintf("%s/admin/orders/count.json?%s", sd.Domain,
params.Encode())
but this is only worthwhile if you have a significant number of params.
If you want to add functions to ShopifyDownloader, why force the caller to define the url structure? Try to make it simpler for them. You don't need to parse urls or write to a bytes buffer, http.Get expects a string, so work with strings. If you want to build functions, consider instead having separate functions which know about the url formats, to abstract this away from the caller, so :
func (sd *ShopifyDownloader) OrderURL(orderID int, params url.Values) string {
params["token"] = []string{sd.AccessToken}
return fmt.Sprintf("%s/admin/orders/%d.json?%s",sd.Domain,orderID,params)
}
The caller should not know about the specific format of Shopify urls, which may change (just as the endpoint may change).

Resources