<commit_before>package gamehack

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/subosito/twilio"
	"github.com/zachlatta/go-tomtom"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
)

type Notification struct {
	UserID           int64             `json:"userId"`
	StorylineUpdates []StorylineUpdate `json:"storylineUpdates"`
}

type StorylineUpdate struct {
	// TODO: Change to equivalent of enum
	Reason          string `json:"reason"`
	LastSegmentType string `json:"lastSegmentType"`
}

type Location struct {
	Lat float64 `json:"lat"`
	Lon float64 `json:"lon"`
}

type Place struct {
	Id       int      `json:"id"`
	Type     string   `json:"type"`
	Location Location `json:"location"`
}

type Segment struct {
	Place      Place  `json:"place"`
	LastUpdate string `json:"lastUpdate"`
}

type DailySegments struct {
	Segments []Segment `json:"segments"`
}

func init() {
	http.HandleFunc("/", root)
	http.HandleFunc("/login", login)
	http.HandleFunc("/logout", logout)
	http.HandleFunc("/phone", phone)
	http.HandleFunc("/addphone", addPhone)
	http.HandleFunc("/delphone", delPhone)
	http.HandleFunc("/authorize", authorize)
	http.HandleFunc("/oauth2callback", oauthCallback)
	http.HandleFunc("/notification", handleNotification)
}

func handleNotification(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	if r.Method != "POST" {
		http.Error(w, "Invalid method.", http.StatusMethodNotAllowed)
		return
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Error reading request body.", http.StatusBadRequest)
		return
	}

	var notification Notification
	err = json.Unmarshal(body, &notification)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	hasDataUpload := false
	for _, update := range notification.StorylineUpdates {
		if update.Reason == "DataUpload" {
			hasDataUpload = true
			break
		}
	}

	if hasDataUpload {
		q := datastore.NewQuery("User").Filter("MovesUserId =", notification.UserID)

		var users []User
		keys, err := q.GetAll(c, &users)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		if len(users) <= 0 {
			http.Error(w, "wtf moves", http.StatusNotFound)
			return
		}

		user, key := users[0], keys[0]

		t := CreateTransport(c, user.MovesToken.NormToken())

		dailySegmentsList, err := GetLatestPlaces(t)
		if err != nil {
			c.Errorf(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		updateDailySegments(*dailySegmentsList, key, w, r)
	}
	/*fmt.Fprintf(w, "%v", notification)
	if err != nil {
		http.Error(w, "Error writing response body.", http.StatusInternalServerError)
		return
	}*/
	/*var place Place
	err = json.Unmarshal(body, &place)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sendText(place, "+15555555555", w, r)*/
}

func updateDailySegments(dailySegmentsList []DailySegments, userKey *datastore.Key, w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	var phoneEntries []PhoneEntry
	_, err := datastore.NewQuery("PhoneEntry").Ancestor(userKey).GetAll(c, &phoneEntries)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	for _, dailySegments := range dailySegmentsList {
		for _, segment := range dailySegments.Segments {
			for _, phone := range phoneEntries {
				sendText(segment.Place, phone.Phone, w, r)
			}
			break // first item in slice is latest one
		}
		break // first item in slice is latest one
	}
}

func sendText(place Place, phone string, w http.ResponseWriter, r *http.Request) {
	a := appengine.NewContext(r)
	f := urlfetch.Client(a)
	c := twilio.NewClient(twilioSid, twilioAuthToken, f)

	c2 := tomtom.NewClient(tomtomKey, nil)
	codes, err := c2.Geocode.ReverseGeocode(place.Location.Lat, place.Location.Lon)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var params twilio.MessageParams
	if len(codes) > 0 {
		params.Body = fmt.Sprintf("I'm now at %s.", codes[0].FormattedAddress)
	} else {
		params.Body = fmt.Sprintf("I'm now at %f, %f.", place.Location.Lat, place.Location.Lon)
	}
	_, _, err = c.Messages.Send(twilioPhone, phone, params)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
}
<commit_msg>reverse geocode bug fix<commit_after>package gamehack

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/subosito/twilio"
	"github.com/zachlatta/go-tomtom"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
)

type Notification struct {
	UserID           int64             `json:"userId"`
	StorylineUpdates []StorylineUpdate `json:"storylineUpdates"`
}

type StorylineUpdate struct {
	// TODO: Change to equivalent of enum
	Reason          string `json:"reason"`
	LastSegmentType string `json:"lastSegmentType"`
}

type Location struct {
	Lat float64 `json:"lat"`
	Lon float64 `json:"lon"`
}

type Place struct {
	Id       int      `json:"id"`
	Type     string   `json:"type"`
	Location Location `json:"location"`
}

type Segment struct {
	Place      Place  `json:"place"`
	LastUpdate string `json:"lastUpdate"`
}

type DailySegments struct {
	Segments []Segment `json:"segments"`
}

func init() {
	http.HandleFunc("/", root)
	http.HandleFunc("/login", login)
	http.HandleFunc("/logout", logout)
	http.HandleFunc("/phone", phone)
	http.HandleFunc("/addphone", addPhone)
	http.HandleFunc("/delphone", delPhone)
	http.HandleFunc("/authorize", authorize)
	http.HandleFunc("/oauth2callback", oauthCallback)
	http.HandleFunc("/notification", handleNotification)
}

func handleNotification(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	if r.Method != "POST" {
		http.Error(w, "Invalid method.", http.StatusMethodNotAllowed)
		return
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Error reading request body.", http.StatusBadRequest)
		return
	}

	var notification Notification
	err = json.Unmarshal(body, &notification)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	hasDataUpload := false
	for _, update := range notification.StorylineUpdates {
		if update.Reason == "DataUpload" {
			hasDataUpload = true
			break
		}
	}

	if hasDataUpload {
		q := datastore.NewQuery("User").Filter("MovesUserId =", notification.UserID)

		var users []User
		keys, err := q.GetAll(c, &users)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		if len(users) <= 0 {
			http.Error(w, "wtf moves", http.StatusNotFound)
			return
		}

		user, key := users[0], keys[0]

		t := CreateTransport(c, user.MovesToken.NormToken())

		dailySegmentsList, err := GetLatestPlaces(t)
		if err != nil {
			c.Errorf(err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		updateDailySegments(*dailySegmentsList, key, w, r)
	}
	/*fmt.Fprintf(w, "%v", notification)
	if err != nil {
		http.Error(w, "Error writing response body.", http.StatusInternalServerError)
		return
	}*/
	/*var place Place
	err = json.Unmarshal(body, &place)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sendText(place, "+15555555555", w, r)*/
}

func updateDailySegments(dailySegmentsList []DailySegments, userKey *datastore.Key, w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	var phoneEntries []PhoneEntry
	_, err := datastore.NewQuery("PhoneEntry").Ancestor(userKey).GetAll(c, &phoneEntries)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	for _, dailySegments := range dailySegmentsList {
		for _, segment := range dailySegments.Segments {
			for _, phone := range phoneEntries {
				sendText(segment.Place, phone.Phone, w, r)
			}
			break // first item in slice is latest one
		}
		break // first item in slice is latest one
	}
}

func sendText(place Place, phone string, w http.ResponseWriter, r *http.Request) {
	a := appengine.NewContext(r)
	f := urlfetch.Client(a)
	c := twilio.NewClient(twilioSid, twilioAuthToken, f)

	c2 := tomtom.NewClient(tomtomKey, f)
	codes, err := c2.Geocode.ReverseGeocode(place.Location.Lat, place.Location.Lon)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var params twilio.MessageParams
	if len(codes) > 0 {
		params.Body = fmt.Sprintf("I'm now at %s.", codes[0].FormattedAddress)
	} else {
		params.Body = fmt.Sprintf("I'm now at %f, %f.", place.Location.Lat, place.Location.Lon)
	}
	_, _, err = c.Messages.Send(twilioPhone, phone, params)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
}
<|endoftext|>


<commit_before>package protocoltypes

import (
	"github.com/cep21/gohelpers/a"
	"github.com/signalfuse/com_signalfuse_metrics_protobuf"
	"github.com/signalfuse/signalfxproxy/core/value"
	"github.com/vektra/errors"
	"os"
	"testing"
)

func TestNewOnHostDatapoint(t *testing.T) {
	hostname, _ := os.Hostname()
	dp := NewOnHostDatapoint("metric", value.NewFloatWire(3.0), com_signalfuse_metrics_protobuf.MetricType_COUNTER)
	a.ExpectEquals(t, hostname, dp.Dimensions()["sf_source"], "Should get source back")

	osXXXHostname = func() (string, error) { return "", errors.New("unable to get hostname") }
	dp = NewOnHostDatapoint("metric", value.NewFloatWire(3.0), com_signalfuse_metrics_protobuf.MetricType_COUNTER)
	a.ExpectEquals(t, "unknown", dp.Dimensions()["sf_source"], "Should get source back")
	osXXXHostname = os.Hostname
}
<commit_msg>Fix wrong errors package<commit_after>package protocoltypes

import (
	"github.com/cep21/gohelpers/a"
	"github.com/signalfuse/com_signalfuse_metrics_protobuf"
	"github.com/signalfuse/signalfxproxy/core/value"
	"os"
	"testing"
	"errors"
)

func TestNewOnHostDatapoint(t *testing.T) {
	hostname, _ := os.Hostname()
	dp := NewOnHostDatapoint("metric", value.NewFloatWire(3.0), com_signalfuse_metrics_protobuf.MetricType_COUNTER)
	a.ExpectEquals(t, hostname, dp.Dimensions()["sf_source"], "Should get source back")

	osXXXHostname = func() (string, error) { return "", errors.New("unable to get hostname") }
	dp = NewOnHostDatapoint("metric", value.NewFloatWire(3.0), com_signalfuse_metrics_protobuf.MetricType_COUNTER)
	a.ExpectEquals(t, "unknown", dp.Dimensions()["sf_source"], "Should get source back")
	osXXXHostname = os.Hostname
}
<|endoftext|>


<commit_before>package auth

import (
	"net/http"
	"time"

	"github.com/gonfire/oauth2"
	"github.com/gonfire/oauth2/bearer"
	"github.com/gonfire/oauth2/hmacsha"
	"gopkg.in/mgo.v2/bson"
)

func (a *Authenticator) AuthorizationEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse authorization request
	req, err := oauth2.ParseAuthorizationRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the response type is known
	if !oauth2.KnownResponseType(req.ResponseType) {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Unknown response type"))
		return
	}

	// get client
	client, err := a.Storage.GetClient(req.ClientID)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if client == nil {
		oauth2.WriteError(w, oauth2.InvalidClient(req.State, "Unknown client"))
		return
	}

	// validate redirect uri
	if !client.ValidRedirectURI(req.RedirectURI) {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Invalid redirect URI"))
		return
	}

	// show notice for a GET request
	if r.Method == "GET" {
		w.Write([]byte("This authentication server does not provide an authorization form.\n" +
			"Please submit the resource owners username and password in a POST request."))
		return
	}

	// triage based on response type
	switch req.ResponseType {
	case oauth2.TokenResponseType:
		if a.Policy.ImplicitGrant {
			a.HandleImplicitGrant(w, r, req, client)
			return
		}
	}

	// response type is unsupported
	oauth2.WriteError(w, oauth2.UnsupportedResponseType(req.State, oauth2.NoDescription))
}

func (a *Authenticator) HandleImplicitGrant(w http.ResponseWriter, r *http.Request, req *oauth2.AuthorizationRequest, client Client) {
	// get credentials
	username := r.PostForm.Get("username")
	password := r.PostForm.Get("password")

	// get resource owner
	resourceOwner, err := a.Storage.GetResourceOwner(username)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
		return
	} else if resourceOwner == nil {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// validate password
	if !resourceOwner.ValidPassword(password) {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// validate & grant scope
	granted, scope := a.Policy.GrantStrategy(&GrantRequest{
		Scope:         req.Scope,
		Client:        client,
		ResourceOwner: resourceOwner,
	})
	if !granted {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// get resource owner id
	rid := resourceOwner.ID()

	// issue access token
	res, err := a.issueTokens(false, scope, req.State, client.ID(), &rid)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.RedirectTokenResponse(w, req.RedirectURI, res)
}

func (a *Authenticator) TokenEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse token request
	req, err := oauth2.ParseTokenRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the grant type is known
	if !oauth2.KnownGrantType(req.GrantType) {
		oauth2.WriteError(w, oauth2.InvalidRequest(oauth2.NoState, "Unknown grant type"))
		return
	}

	// get client
	client, err := a.Storage.GetClient(req.ClientID)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if client == nil {
		oauth2.WriteError(w, oauth2.InvalidClient(oauth2.NoState, "Unknown client"))
		return
	}

	//// authenticate client
	//if client.confidential && !sameHash(client.secret, req.ClientSecret) {
	//	oauth2.WriteError(w, oauth2.InvalidClient(oauth2.NoState, "Unknown client"))
	//	return
	//}

	// handle grant type
	switch req.GrantType {
	case oauth2.PasswordGrantType:
		if a.Policy.PasswordGrant {
			a.HandleResourceOwnerPasswordCredentialsGrant(w, req, client)
			return
		}
	case oauth2.ClientCredentialsGrantType:
		if a.Policy.ClientCredentialsGrant {
			a.HandleClientCredentialsGrant(w, req, client)
			return
		}
	case oauth2.RefreshTokenGrantType:
		a.HandleRefreshTokenGrant(w, req, client)
		return
	}

	// grant type is unsupported
	oauth2.WriteError(w, oauth2.UnsupportedGrantType(oauth2.NoState, oauth2.NoDescription))
}

func (a *Authenticator) HandleResourceOwnerPasswordCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest, client Client) {
	// get resource owner
	resourceOwner, err := a.Storage.GetResourceOwner(req.Username)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if resourceOwner == nil {
		oauth2.WriteError(w, oauth2.AccessDenied(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// authenticate resource owner
	if !resourceOwner.ValidPassword(req.Password) {
		oauth2.WriteError(w, oauth2.AccessDenied(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// validate & grant scope
	granted, scope := a.Policy.GrantStrategy(&GrantRequest{
		Scope:         req.Scope,
		Client:        client,
		ResourceOwner: resourceOwner,
	})
	if !granted {
		oauth2.WriteError(w, oauth2.InvalidScope(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// get resource owner id
	rid := resourceOwner.ID()

	// issue access token
	res, err := a.issueTokens(true, scope, oauth2.NoState, client.ID(), &rid)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func (a *Authenticator) HandleClientCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest, client Client) {
	// authenticate client
	if !client.ValidSecret(req.ClientSecret) {
		oauth2.WriteError(w, oauth2.InvalidClient(oauth2.NoState, "Unknown client"))
		return
	}

	// validate & grant scope
	granted, scope := a.Policy.GrantStrategy(&GrantRequest{
		Scope:  req.Scope,
		Client: client,
	})
	if !granted {
		oauth2.WriteError(w, oauth2.InvalidScope(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// issue access token
	res, err := a.issueTokens(true, scope, oauth2.NoState, client.ID(), nil)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func (a *Authenticator) HandleRefreshTokenGrant(w http.ResponseWriter, req *oauth2.TokenRequest, client Client) {
	// parse refresh token
	refreshToken, err := hmacsha.Parse(a.Policy.Secret, req.RefreshToken)
	if err != nil {
		oauth2.WriteError(w, oauth2.InvalidRequest(oauth2.NoState, err.Error()))
		return
	}

	// get stored refresh token by signature
	rt, err := a.Storage.GetRefreshToken(refreshToken.SignatureString())
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if rt == nil {
		oauth2.WriteError(w, oauth2.InvalidGrant(oauth2.NoState, "Unknown refresh token"))
		return
	}

	// get data
	data := rt.GetTokenData()

	// validate expiration
	if data.ExpiresAt.Before(time.Now()) {
		oauth2.WriteError(w, oauth2.InvalidGrant(oauth2.NoState, "Expired refresh token"))
		return
	}

	// validate ownership
	if data.ClientID != client.ID() {
		oauth2.WriteError(w, oauth2.InvalidGrant(oauth2.NoState, "Invalid refresh token ownership"))
		return
	}

	// inherit scope from stored refresh token
	if req.Scope.Empty() {
		req.Scope = data.Scope
	}

	// validate scope - a missing scope is always included
	if !data.Scope.Includes(req.Scope) {
		oauth2.WriteError(w, oauth2.InvalidScope(oauth2.NoState, "Scope exceeds the originally granted scope"))
		return
	}

	// issue tokens
	res, err := a.issueTokens(true, req.Scope, oauth2.NoState, client.ID(), data.ResourceOwnerID)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// delete refresh token
	err = a.Storage.DeleteRefreshToken(refreshToken.SignatureString())
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func (a *Authenticator) issueTokens(issueRefreshToken bool, scope oauth2.Scope, state string, clientID bson.ObjectId, resourceOwnerID *bson.ObjectId) (*oauth2.TokenResponse, error) {
	// generate new access token
	accessToken, err := hmacsha.Generate(a.Policy.Secret, 32)
	if err != nil {
		return nil, err
	}

	// generate new refresh token
	refreshToken, err := hmacsha.Generate(a.Policy.Secret, 32)
	if err != nil {
		return nil, err
	}

	// prepare response
	res := bearer.NewTokenResponse(accessToken.String(), int(a.Policy.AccessTokenLifespan/time.Second))

	// set granted scope
	res.Scope = scope

	// set state
	res.State = state

	// set refresh token if requested
	if issueRefreshToken {
		res.RefreshToken = refreshToken.String()
	}

	// create access token data
	accessTokenData := &TokenData{
		Signature:       accessToken.SignatureString(),
		Scope:           scope,
		ExpiresAt:       time.Now().Add(a.Policy.AccessTokenLifespan),
		ClientID:        clientID,
		ResourceOwnerID: resourceOwnerID,
	}

	// save access token
	_, err = a.Storage.SaveAccessToken(accessTokenData)
	if err != nil {
		return nil, err
	}

	if issueRefreshToken {
		// create refresh token data
		refreshTokenData := &TokenData{
			Signature:       refreshToken.SignatureString(),
			Scope:           scope,
			ExpiresAt:       time.Now().Add(a.Policy.RefreshTokenLifespan),
			ClientID:        clientID,
			ResourceOwnerID: resourceOwnerID,
		}

		// save refresh token
		_, err := a.Storage.SaveRefreshToken(refreshTokenData)
		if err != nil {
			return nil, err
		}
	}

	return res, nil
}
<commit_msg>check request method in grant handler<commit_after>package auth

import (
	"net/http"
	"time"

	"github.com/gonfire/oauth2"
	"github.com/gonfire/oauth2/bearer"
	"github.com/gonfire/oauth2/hmacsha"
	"gopkg.in/mgo.v2/bson"
)

func (a *Authenticator) AuthorizationEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse authorization request
	req, err := oauth2.ParseAuthorizationRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the response type is known
	if !oauth2.KnownResponseType(req.ResponseType) {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Unknown response type"))
		return
	}

	// get client
	client, err := a.Storage.GetClient(req.ClientID)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if client == nil {
		oauth2.WriteError(w, oauth2.InvalidClient(req.State, "Unknown client"))
		return
	}

	// validate redirect uri
	if !client.ValidRedirectURI(req.RedirectURI) {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Invalid redirect URI"))
		return
	}

	// triage based on response type
	switch req.ResponseType {
	case oauth2.TokenResponseType:
		if a.Policy.ImplicitGrant {
			a.HandleImplicitGrant(w, r, req, client)
			return
		}
	}

	// response type is unsupported
	oauth2.WriteError(w, oauth2.UnsupportedResponseType(req.State, oauth2.NoDescription))
}

func (a *Authenticator) HandleImplicitGrant(w http.ResponseWriter, r *http.Request, req *oauth2.AuthorizationRequest, client Client) {
	// check request method
	if r.Method == "GET" {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.InvalidRequest(req.State, "Unallowed request method"))
		return
	}

	// get credentials
	username := r.PostForm.Get("username")
	password := r.PostForm.Get("password")

	// get resource owner
	resourceOwner, err := a.Storage.GetResourceOwner(username)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
		return
	} else if resourceOwner == nil {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// validate password
	if !resourceOwner.ValidPassword(password) {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// validate & grant scope
	granted, scope := a.Policy.GrantStrategy(&GrantRequest{
		Scope:         req.Scope,
		Client:        client,
		ResourceOwner: resourceOwner,
	})
	if !granted {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// get resource owner id
	rid := resourceOwner.ID()

	// issue access token
	res, err := a.issueTokens(false, scope, req.State, client.ID(), &rid)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.RedirectTokenResponse(w, req.RedirectURI, res)
}

func (a *Authenticator) TokenEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse token request
	req, err := oauth2.ParseTokenRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the grant type is known
	if !oauth2.KnownGrantType(req.GrantType) {
		oauth2.WriteError(w, oauth2.InvalidRequest(oauth2.NoState, "Unknown grant type"))
		return
	}

	// get client
	client, err := a.Storage.GetClient(req.ClientID)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if client == nil {
		oauth2.WriteError(w, oauth2.InvalidClient(oauth2.NoState, "Unknown client"))
		return
	}

	// handle grant type
	switch req.GrantType {
	case oauth2.PasswordGrantType:
		if a.Policy.PasswordGrant {
			a.HandleResourceOwnerPasswordCredentialsGrant(w, req, client)
			return
		}
	case oauth2.ClientCredentialsGrantType:
		if a.Policy.ClientCredentialsGrant {
			a.HandleClientCredentialsGrant(w, req, client)
			return
		}
	case oauth2.RefreshTokenGrantType:
		a.HandleRefreshTokenGrant(w, req, client)
		return
	}

	// grant type is unsupported
	oauth2.WriteError(w, oauth2.UnsupportedGrantType(oauth2.NoState, oauth2.NoDescription))
}

func (a *Authenticator) HandleResourceOwnerPasswordCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest, client Client) {
	// get resource owner
	resourceOwner, err := a.Storage.GetResourceOwner(req.Username)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if resourceOwner == nil {
		oauth2.WriteError(w, oauth2.AccessDenied(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// authenticate resource owner
	if !resourceOwner.ValidPassword(req.Password) {
		oauth2.WriteError(w, oauth2.AccessDenied(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// validate & grant scope
	granted, scope := a.Policy.GrantStrategy(&GrantRequest{
		Scope:         req.Scope,
		Client:        client,
		ResourceOwner: resourceOwner,
	})
	if !granted {
		oauth2.WriteError(w, oauth2.InvalidScope(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// get resource owner id
	rid := resourceOwner.ID()

	// issue access token
	res, err := a.issueTokens(true, scope, oauth2.NoState, client.ID(), &rid)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func (a *Authenticator) HandleClientCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest, client Client) {
	// authenticate client
	if !client.ValidSecret(req.ClientSecret) {
		oauth2.WriteError(w, oauth2.InvalidClient(oauth2.NoState, "Unknown client"))
		return
	}

	// validate & grant scope
	granted, scope := a.Policy.GrantStrategy(&GrantRequest{
		Scope:  req.Scope,
		Client: client,
	})
	if !granted {
		oauth2.WriteError(w, oauth2.InvalidScope(oauth2.NoState, oauth2.NoDescription))
		return
	}

	// issue access token
	res, err := a.issueTokens(true, scope, oauth2.NoState, client.ID(), nil)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func (a *Authenticator) HandleRefreshTokenGrant(w http.ResponseWriter, req *oauth2.TokenRequest, client Client) {
	// parse refresh token
	refreshToken, err := hmacsha.Parse(a.Policy.Secret, req.RefreshToken)
	if err != nil {
		oauth2.WriteError(w, oauth2.InvalidRequest(oauth2.NoState, err.Error()))
		return
	}

	// get stored refresh token by signature
	rt, err := a.Storage.GetRefreshToken(refreshToken.SignatureString())
	if err != nil {
		oauth2.WriteError(w, err)
		return
	} else if rt == nil {
		oauth2.WriteError(w, oauth2.InvalidGrant(oauth2.NoState, "Unknown refresh token"))
		return
	}

	// get data
	data := rt.GetTokenData()

	// validate expiration
	if data.ExpiresAt.Before(time.Now()) {
		oauth2.WriteError(w, oauth2.InvalidGrant(oauth2.NoState, "Expired refresh token"))
		return
	}

	// validate ownership
	if data.ClientID != client.ID() {
		oauth2.WriteError(w, oauth2.InvalidGrant(oauth2.NoState, "Invalid refresh token ownership"))
		return
	}

	// inherit scope from stored refresh token
	if req.Scope.Empty() {
		req.Scope = data.Scope
	}

	// validate scope - a missing scope is always included
	if !data.Scope.Includes(req.Scope) {
		oauth2.WriteError(w, oauth2.InvalidScope(oauth2.NoState, "Scope exceeds the originally granted scope"))
		return
	}

	// issue tokens
	res, err := a.issueTokens(true, req.Scope, oauth2.NoState, client.ID(), data.ResourceOwnerID)
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// delete refresh token
	err = a.Storage.DeleteRefreshToken(refreshToken.SignatureString())
	if err != nil {
		oauth2.RedirectError(w, req.RedirectURI, true, err)
	}

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func (a *Authenticator) issueTokens(issueRefreshToken bool, scope oauth2.Scope, state string, clientID bson.ObjectId, resourceOwnerID *bson.ObjectId) (*oauth2.TokenResponse, error) {
	// generate new access token
	accessToken, err := hmacsha.Generate(a.Policy.Secret, 32)
	if err != nil {
		return nil, err
	}

	// generate new refresh token
	refreshToken, err := hmacsha.Generate(a.Policy.Secret, 32)
	if err != nil {
		return nil, err
	}

	// prepare response
	res := bearer.NewTokenResponse(accessToken.String(), int(a.Policy.AccessTokenLifespan/time.Second))

	// set granted scope
	res.Scope = scope

	// set state
	res.State = state

	// set refresh token if requested
	if issueRefreshToken {
		res.RefreshToken = refreshToken.String()
	}

	// create access token data
	accessTokenData := &TokenData{
		Signature:       accessToken.SignatureString(),
		Scope:           scope,
		ExpiresAt:       time.Now().Add(a.Policy.AccessTokenLifespan),
		ClientID:        clientID,
		ResourceOwnerID: resourceOwnerID,
	}

	// save access token
	_, err = a.Storage.SaveAccessToken(accessTokenData)
	if err != nil {
		return nil, err
	}

	if issueRefreshToken {
		// create refresh token data
		refreshTokenData := &TokenData{
			Signature:       refreshToken.SignatureString(),
			Scope:           scope,
			ExpiresAt:       time.Now().Add(a.Policy.RefreshTokenLifespan),
			ClientID:        clientID,
			ResourceOwnerID: resourceOwnerID,
		}

		// save refresh token
		_, err := a.Storage.SaveRefreshToken(refreshTokenData)
		if err != nil {
			return nil, err
		}
	}

	return res, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package oidc

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/coreos/go-oidc/jose"
	"github.com/coreos/go-oidc/key"
	"github.com/coreos/go-oidc/oidc"
	"k8s.io/kubernetes/pkg/auth/user"
)

type oidcProvider struct {
	mux     *http.ServeMux
	pcfg    oidc.ProviderConfig
	privKey *key.PrivateKey
}

func newOIDCProvider(t *testing.T) *oidcProvider {
	privKey, err := key.GeneratePrivateKey()
	if err != nil {
		t.Fatalf("Cannot create OIDC Provider: %v", err)
		return nil
	}

	op := &oidcProvider{
		mux:     http.NewServeMux(),
		privKey: privKey,
	}

	op.mux.HandleFunc("/.well-known/openid-configuration", op.handleConfig)
	op.mux.HandleFunc("/keys", op.handleKeys)

	return op

}

func (op *oidcProvider) handleConfig(w http.ResponseWriter, req *http.Request) {
	b, err := json.Marshal(op.pcfg)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(b)
}

func (op *oidcProvider) handleKeys(w http.ResponseWriter, req *http.Request) {
	keys := struct {
		Keys []jose.JWK `json:"keys"`
	}{
		Keys: []jose.JWK{op.privKey.JWK()},
	}

	b, err := json.Marshal(keys)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(time.Hour.Seconds())))
	w.Header().Set("Expires", time.Now().Add(time.Hour).Format(time.RFC1123))
	w.Header().Set("Content-Type", "application/json")
	w.Write(b)
}

func (op *oidcProvider) generateToken(t *testing.T, iss, sub, aud string, usernameClaim, value string, iat, exp time.Time) string {
	signer := op.privKey.Signer()
	claims := oidc.NewClaims(iss, sub, aud, iat, exp)
	claims.Add(usernameClaim, value)

	jwt, err := jose.NewSignedJWT(claims, signer)
	if err != nil {
		t.Fatalf("Cannot generate token: %v", err)
		return ""
	}
	return jwt.Encode()
}

func (op *oidcProvider) generateGoodToken(t *testing.T, iss, sub, aud string, usernameClaim, value string) string {
	return op.generateToken(t, iss, sub, aud, usernameClaim, value, time.Now(), time.Now().Add(time.Hour))
}

func (op *oidcProvider) generateMalformedToken(t *testing.T, iss, sub, aud string, usernameClaim, value string) string {
	return op.generateToken(t, iss, sub, aud, usernameClaim, value, time.Now(), time.Now().Add(time.Hour)) + "randombits"
}

func (op *oidcProvider) generateExpiredToken(t *testing.T, iss, sub, aud string, usernameClaim, value string) string {
	return op.generateToken(t, iss, sub, aud, usernameClaim, value, time.Now().Add(-2*time.Hour), time.Now().Add(-1*time.Hour))
}

// generateSelfSignedCert generates a self-signed cert/key pairs and writes to the certPath/keyPath.
// This method is mostly identical to util.GenerateSelfSignedCert except for the 'IsCA' and 'KeyUsage'
// in the certificate template. (Maybe we can merge these two methods).
func generateSelfSignedCert(t *testing.T, host, certPath, keyPath string) {
	priv, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		t.Fatal(err)
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		t.Fatal(err)
	}

	// Generate cert
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
		t.Fatal(err)
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		t.Fatal(err)
	}

	// Write cert
	if err := os.MkdirAll(filepath.Dir(certPath), os.FileMode(0755)); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(certPath, certBuffer.Bytes(), os.FileMode(0644)); err != nil {
		t.Fatal(err)
	}

	// Write key
	if err := os.MkdirAll(filepath.Dir(keyPath), os.FileMode(0755)); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(keyPath, keyBuffer.Bytes(), os.FileMode(0600)); err != nil {
		t.Fatal(err)
	}
}

func TestOIDCDiscoveryTimeout(t *testing.T) {
	maxRetries = 3
	retryBackoff = time.Second
	expectErr := fmt.Errorf("failed to fetch provider config after 3 retries")

	_, err := New("https://foo/bar", "client-foo", "", "sub")
	if !reflect.DeepEqual(err, expectErr) {
		t.Errorf("Expecting %v, but got %v", expectErr, err)
	}
}

func TestOIDCDiscoveryNoKeyEndpoint(t *testing.T) {
	var err error
	expectErr := fmt.Errorf("OIDC provider must provide 'jwks_uri' for public key discovery")

	cert := path.Join(os.TempDir(), "oidc-cert")
	key := path.Join(os.TempDir(), "oidc-key")

	defer os.Remove(cert)
	defer os.Remove(key)

	generateSelfSignedCert(t, "127.0.0.1", cert, key)

	op := newOIDCProvider(t)
	srv := httptest.NewUnstartedServer(op.mux)
	srv.TLS = &tls.Config{Certificates: make([]tls.Certificate, 1)}
	srv.TLS.Certificates[0], err = tls.LoadX509KeyPair(cert, key)
	if err != nil {
		t.Fatalf("Cannot load cert/key pair: %v", err)
	}
	srv.StartTLS()
	defer srv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer: srv.URL,
	}

	_, err = New(srv.URL, "client-foo", cert, "sub")
	if !reflect.DeepEqual(err, expectErr) {
		t.Errorf("Expecting %v, but got %v", expectErr, err)
	}
}

func TestOIDCDiscoverySecureConnection(t *testing.T) {
	maxRetries = 3
	retryBackoff = time.Second

	// Verify that plain HTTP issuer URL is forbidden.
	op := newOIDCProvider(t)
	srv := httptest.NewServer(op.mux)
	defer srv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer:       srv.URL,
		KeysEndpoint: srv.URL + "/keys",
	}

	expectErr := fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", srv.URL, "http")

	_, err := New(srv.URL, "client-foo", "", "sub")
	if !reflect.DeepEqual(err, expectErr) {
		t.Errorf("Expecting %v, but got %v", expectErr, err)
	}

	// Verify the cert/key pair works.
	cert1 := path.Join(os.TempDir(), "oidc-cert-1")
	key1 := path.Join(os.TempDir(), "oidc-key-1")
	cert2 := path.Join(os.TempDir(), "oidc-cert-2")
	key2 := path.Join(os.TempDir(), "oidc-key-2")

	defer os.Remove(cert1)
	defer os.Remove(key1)
	defer os.Remove(cert2)
	defer os.Remove(key2)

	generateSelfSignedCert(t, "127.0.0.1", cert1, key1)
	generateSelfSignedCert(t, "127.0.0.1", cert2, key2)

	// Create a TLS server using cert/key pair 1.
	tlsSrv := httptest.NewUnstartedServer(op.mux)
	tlsSrv.TLS = &tls.Config{Certificates: make([]tls.Certificate, 1)}
	tlsSrv.TLS.Certificates[0], err = tls.LoadX509KeyPair(cert1, key1)
	if err != nil {
		t.Fatalf("Cannot load cert/key pair: %v", err)
	}
	tlsSrv.StartTLS()
	defer tlsSrv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer:       tlsSrv.URL,
		KeysEndpoint: tlsSrv.URL + "/keys",
	}

	// Create a client using cert2, should fail.
	_, err = New(tlsSrv.URL, "client-foo", cert2, "sub")
	if err == nil {
		t.Fatalf("Expecting error, but got nothing")
	}

}

func TestOIDCAuthentication(t *testing.T) {
	var err error

	cert := path.Join(os.TempDir(), "oidc-cert")
	key := path.Join(os.TempDir(), "oidc-key")

	defer os.Remove(cert)
	defer os.Remove(key)

	generateSelfSignedCert(t, "127.0.0.1", cert, key)

	// Create a TLS server and a client.
	op := newOIDCProvider(t)
	srv := httptest.NewUnstartedServer(op.mux)
	srv.TLS = &tls.Config{Certificates: make([]tls.Certificate, 1)}
	srv.TLS.Certificates[0], err = tls.LoadX509KeyPair(cert, key)
	if err != nil {
		t.Fatalf("Cannot load cert/key pair: %v", err)
	}
	srv.StartTLS()
	defer srv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer:       srv.URL,
		KeysEndpoint: srv.URL + "/keys",
	}

	tests := []struct {
		userClaim string
		token     string
		userInfo  user.Info
		verified  bool
		err       string
	}{
		{
			"sub",
			op.generateGoodToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo"),
			&user.DefaultInfo{Name: fmt.Sprintf("%s#%s", srv.URL, "user-foo")},
			true,
			"",
		},
		{
			// Use user defined claim (email here).
			"email",
			op.generateGoodToken(t, srv.URL, "client-foo", "client-foo", "email", "foo@example.com"),
			&user.DefaultInfo{Name: "foo@example.com"},
			true,
			"",
		},
		{
			"sub",
			op.generateMalformedToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo"),
			nil,
			false,
			"malformed JWS, unable to decode signature",
		},
		{
			// Invalid 'aud'.
			"sub",
			op.generateGoodToken(t, srv.URL, "client-foo", "client-bar", "sub", "user-foo"),
			nil,
			false,
			"oidc: JWT claims invalid: invalid claim value: 'aud'",
		},
		{
			// Invalid issuer.
			"sub",
			op.generateGoodToken(t, "http://foo-bar.com", "client-foo", "client-foo", "sub", "user-foo"),
			nil,
			false,
			"oidc: JWT claims invalid: invalid claim value: 'iss'.",
		},
		{
			"sub",
			op.generateExpiredToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo"),
			nil,
			false,
			"oidc: JWT claims invalid: token is expired",
		},
	}

	for i, tt := range tests {
		client, err := New(srv.URL, "client-foo", cert, tt.userClaim)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		user, result, err := client.AuthenticateToken(tt.token)
		if tt.err != "" {
			if !strings.HasPrefix(err.Error(), tt.err) {
				t.Errorf("#%d: Expecting: %v..., but got: %v", i, tt.err, err)
			}
		} else {
			if err != nil {
				t.Errorf("#%d: Unexpected error: %v", i, err)
			}
		}
		if !reflect.DeepEqual(tt.verified, result) {
			t.Errorf("#%d: Expecting: %v, but got: %v", i, tt.verified, result)
		}
		if !reflect.DeepEqual(tt.userInfo, user) {
			t.Errorf("#%d: Expecting: %v, but got: %v", i, tt.userInfo, user)
		}
	}
}
<commit_msg>UPSTREAM: <drop>: disable oidc tests<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package oidc

/*
import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/coreos/go-oidc/jose"
	"github.com/coreos/go-oidc/key"
	"github.com/coreos/go-oidc/oidc"
	"k8s.io/kubernetes/pkg/auth/user"
)

type oidcProvider struct {
	mux     *http.ServeMux
	pcfg    oidc.ProviderConfig
	privKey *key.PrivateKey
}

func newOIDCProvider(t *testing.T) *oidcProvider {
	privKey, err := key.GeneratePrivateKey()
	if err != nil {
		t.Fatalf("Cannot create OIDC Provider: %v", err)
		return nil
	}

	op := &oidcProvider{
		mux:     http.NewServeMux(),
		privKey: privKey,
	}

	op.mux.HandleFunc("/.well-known/openid-configuration", op.handleConfig)
	op.mux.HandleFunc("/keys", op.handleKeys)

	return op

}

func (op *oidcProvider) handleConfig(w http.ResponseWriter, req *http.Request) {
	b, err := json.Marshal(op.pcfg)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(b)
}

func (op *oidcProvider) handleKeys(w http.ResponseWriter, req *http.Request) {
	keys := struct {
		Keys []jose.JWK `json:"keys"`
	}{
		Keys: []jose.JWK{op.privKey.JWK()},
	}

	b, err := json.Marshal(keys)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(time.Hour.Seconds())))
	w.Header().Set("Expires", time.Now().Add(time.Hour).Format(time.RFC1123))
	w.Header().Set("Content-Type", "application/json")
	w.Write(b)
}

func (op *oidcProvider) generateToken(t *testing.T, iss, sub, aud string, usernameClaim, value string, iat, exp time.Time) string {
	signer := op.privKey.Signer()
	claims := oidc.NewClaims(iss, sub, aud, iat, exp)
	claims.Add(usernameClaim, value)

	jwt, err := jose.NewSignedJWT(claims, signer)
	if err != nil {
		t.Fatalf("Cannot generate token: %v", err)
		return ""
	}
	return jwt.Encode()
}

func (op *oidcProvider) generateGoodToken(t *testing.T, iss, sub, aud string, usernameClaim, value string) string {
	return op.generateToken(t, iss, sub, aud, usernameClaim, value, time.Now(), time.Now().Add(time.Hour))
}

func (op *oidcProvider) generateMalformedToken(t *testing.T, iss, sub, aud string, usernameClaim, value string) string {
	return op.generateToken(t, iss, sub, aud, usernameClaim, value, time.Now(), time.Now().Add(time.Hour)) + "randombits"
}

func (op *oidcProvider) generateExpiredToken(t *testing.T, iss, sub, aud string, usernameClaim, value string) string {
	return op.generateToken(t, iss, sub, aud, usernameClaim, value, time.Now().Add(-2*time.Hour), time.Now().Add(-1*time.Hour))
}

// generateSelfSignedCert generates a self-signed cert/key pairs and writes to the certPath/keyPath.
// This method is mostly identical to util.GenerateSelfSignedCert except for the 'IsCA' and 'KeyUsage'
// in the certificate template. (Maybe we can merge these two methods).
func generateSelfSignedCert(t *testing.T, host, certPath, keyPath string) {
	priv, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		t.Fatal(err)
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		t.Fatal(err)
	}

	// Generate cert
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
		t.Fatal(err)
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		t.Fatal(err)
	}

	// Write cert
	if err := os.MkdirAll(filepath.Dir(certPath), os.FileMode(0755)); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(certPath, certBuffer.Bytes(), os.FileMode(0644)); err != nil {
		t.Fatal(err)
	}

	// Write key
	if err := os.MkdirAll(filepath.Dir(keyPath), os.FileMode(0755)); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(keyPath, keyBuffer.Bytes(), os.FileMode(0600)); err != nil {
		t.Fatal(err)
	}
}

func TestOIDCDiscoveryTimeout(t *testing.T) {
	maxRetries = 3
	retryBackoff = time.Second
	expectErr := fmt.Errorf("failed to fetch provider config after 3 retries")

	_, err := New("https://foo/bar", "client-foo", "", "sub")
	if !reflect.DeepEqual(err, expectErr) {
		t.Errorf("Expecting %v, but got %v", expectErr, err)
	}
}

func TestOIDCDiscoveryNoKeyEndpoint(t *testing.T) {
	var err error
	expectErr := fmt.Errorf("OIDC provider must provide 'jwks_uri' for public key discovery")

	cert := path.Join(os.TempDir(), "oidc-cert")
	key := path.Join(os.TempDir(), "oidc-key")

	defer os.Remove(cert)
	defer os.Remove(key)

	generateSelfSignedCert(t, "127.0.0.1", cert, key)

	op := newOIDCProvider(t)
	srv := httptest.NewUnstartedServer(op.mux)
	srv.TLS = &tls.Config{Certificates: make([]tls.Certificate, 1)}
	srv.TLS.Certificates[0], err = tls.LoadX509KeyPair(cert, key)
	if err != nil {
		t.Fatalf("Cannot load cert/key pair: %v", err)
	}
	srv.StartTLS()
	defer srv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer: srv.URL,
	}

	_, err = New(srv.URL, "client-foo", cert, "sub")
	if !reflect.DeepEqual(err, expectErr) {
		t.Errorf("Expecting %v, but got %v", expectErr, err)
	}
}

func TestOIDCDiscoverySecureConnection(t *testing.T) {
	maxRetries = 3
	retryBackoff = time.Second

	// Verify that plain HTTP issuer URL is forbidden.
	op := newOIDCProvider(t)
	srv := httptest.NewServer(op.mux)
	defer srv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer:       srv.URL,
		KeysEndpoint: srv.URL + "/keys",
	}

	expectErr := fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", srv.URL, "http")

	_, err := New(srv.URL, "client-foo", "", "sub")
	if !reflect.DeepEqual(err, expectErr) {
		t.Errorf("Expecting %v, but got %v", expectErr, err)
	}

	// Verify the cert/key pair works.
	cert1 := path.Join(os.TempDir(), "oidc-cert-1")
	key1 := path.Join(os.TempDir(), "oidc-key-1")
	cert2 := path.Join(os.TempDir(), "oidc-cert-2")
	key2 := path.Join(os.TempDir(), "oidc-key-2")

	defer os.Remove(cert1)
	defer os.Remove(key1)
	defer os.Remove(cert2)
	defer os.Remove(key2)

	generateSelfSignedCert(t, "127.0.0.1", cert1, key1)
	generateSelfSignedCert(t, "127.0.0.1", cert2, key2)

	// Create a TLS server using cert/key pair 1.
	tlsSrv := httptest.NewUnstartedServer(op.mux)
	tlsSrv.TLS = &tls.Config{Certificates: make([]tls.Certificate, 1)}
	tlsSrv.TLS.Certificates[0], err = tls.LoadX509KeyPair(cert1, key1)
	if err != nil {
		t.Fatalf("Cannot load cert/key pair: %v", err)
	}
	tlsSrv.StartTLS()
	defer tlsSrv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer:       tlsSrv.URL,
		KeysEndpoint: tlsSrv.URL + "/keys",
	}

	// Create a client using cert2, should fail.
	_, err = New(tlsSrv.URL, "client-foo", cert2, "sub")
	if err == nil {
		t.Fatalf("Expecting error, but got nothing")
	}

}

func TestOIDCAuthentication(t *testing.T) {
	var err error

	cert := path.Join(os.TempDir(), "oidc-cert")
	key := path.Join(os.TempDir(), "oidc-key")

	defer os.Remove(cert)
	defer os.Remove(key)

	generateSelfSignedCert(t, "127.0.0.1", cert, key)

	// Create a TLS server and a client.
	op := newOIDCProvider(t)
	srv := httptest.NewUnstartedServer(op.mux)
	srv.TLS = &tls.Config{Certificates: make([]tls.Certificate, 1)}
	srv.TLS.Certificates[0], err = tls.LoadX509KeyPair(cert, key)
	if err != nil {
		t.Fatalf("Cannot load cert/key pair: %v", err)
	}
	srv.StartTLS()
	defer srv.Close()

	op.pcfg = oidc.ProviderConfig{
		Issuer:       srv.URL,
		KeysEndpoint: srv.URL + "/keys",
	}

	tests := []struct {
		userClaim string
		token     string
		userInfo  user.Info
		verified  bool
		err       string
	}{
		{
			"sub",
			op.generateGoodToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo"),
			&user.DefaultInfo{Name: fmt.Sprintf("%s#%s", srv.URL, "user-foo")},
			true,
			"",
		},
		{
			// Use user defined claim (email here).
			"email",
			op.generateGoodToken(t, srv.URL, "client-foo", "client-foo", "email", "foo@example.com"),
			&user.DefaultInfo{Name: "foo@example.com"},
			true,
			"",
		},
		{
			"sub",
			op.generateMalformedToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo"),
			nil,
			false,
			"malformed JWS, unable to decode signature",
		},
		{
			// Invalid 'aud'.
			"sub",
			op.generateGoodToken(t, srv.URL, "client-foo", "client-bar", "sub", "user-foo"),
			nil,
			false,
			"oidc: JWT claims invalid: invalid claim value: 'aud'",
		},
		{
			// Invalid issuer.
			"sub",
			op.generateGoodToken(t, "http://foo-bar.com", "client-foo", "client-foo", "sub", "user-foo"),
			nil,
			false,
			"oidc: JWT claims invalid: invalid claim value: 'iss'.",
		},
		{
			"sub",
			op.generateExpiredToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo"),
			nil,
			false,
			"oidc: JWT claims invalid: token is expired",
		},
	}

	for i, tt := range tests {
		client, err := New(srv.URL, "client-foo", cert, tt.userClaim)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		user, result, err := client.AuthenticateToken(tt.token)
		if tt.err != "" {
			if !strings.HasPrefix(err.Error(), tt.err) {
				t.Errorf("#%d: Expecting: %v..., but got: %v", i, tt.err, err)
			}
		} else {
			if err != nil {
				t.Errorf("#%d: Unexpected error: %v", i, err)
			}
		}
		if !reflect.DeepEqual(tt.verified, result) {
			t.Errorf("#%d: Expecting: %v, but got: %v", i, tt.verified, result)
		}
		if !reflect.DeepEqual(tt.userInfo, user) {
			t.Errorf("#%d: Expecting: %v, but got: %v", i, tt.userInfo, user)
		}
	}
}
*/
<|endoftext|>


<commit_before>// Package shell defines code that is common in shells
package shell

import "github.com/hashicorp/packer/common"

// Provisioner contains common fields to all provisioners
type Provisioner struct {
	common.PackerConfig `mapstructure:",squash"`

	// If true, the script contains binary and line endings will not be
	// converted from Windows to Unix-style.
	Binary bool

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes, {{ .Vars }}
	// can be used to inject the environment_vars into the environment.
	ExecuteCommand string `mapstructure:"execute_command"`

	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	// This defaults to remote_folder/remote_file
	RemotePath string `mapstructure:"remote_path"`

	// The local path of the shell script to upload and execute.
	Script string

	// An array of multiple scripts to run.
	Scripts []string

	// Valid Exit Codes - 0 is not always the only valid error code!  See
	// http://www.symantec.com/connect/articles/windows-system-error-codes-exit-codes-description
	// for examples such as 3010 - "The requested operation is successful.
	ValidExitCodes []int `mapstructure:"valid_exit_codes"`

	// An array of environment variables that will be injected before
	// your command(s) are executed.
	Vars []string `mapstructure:"environment_vars"`
}
<commit_msg>Update common/shell/shell.go<commit_after>// Package shell defines code that is common in shells
package shell

import "github.com/hashicorp/packer/common"

// Provisioner contains common fields to all shell provisioners
type Provisioner struct {
	common.PackerConfig `mapstructure:",squash"`

	// If true, the script contains binary and line endings will not be
	// converted from Windows to Unix-style.
	Binary bool

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes, {{ .Vars }}
	// can be used to inject the environment_vars into the environment.
	ExecuteCommand string `mapstructure:"execute_command"`

	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	// This defaults to remote_folder/remote_file
	RemotePath string `mapstructure:"remote_path"`

	// The local path of the shell script to upload and execute.
	Script string

	// An array of multiple scripts to run.
	Scripts []string

	// Valid Exit Codes - 0 is not always the only valid error code!  See
	// http://www.symantec.com/connect/articles/windows-system-error-codes-exit-codes-description
	// for examples such as 3010 - "The requested operation is successful.
	ValidExitCodes []int `mapstructure:"valid_exit_codes"`

	// An array of environment variables that will be injected before
	// your command(s) are executed.
	Vars []string `mapstructure:"environment_vars"`
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"path"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/sets"

	v1 "k8s.io/test-infra/prow/apis/prowjobs/v1"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/gcsupload"
	"k8s.io/test-infra/prow/git/v2"
	"k8s.io/test-infra/prow/io"
	"k8s.io/test-infra/prow/pod-utils/downwardapi"
)

var pullCommitRe = regexp.MustCompile(`^[-\w]+:\w{40},\d+:(\w{40})$`)

type prHistoryTemplate struct {
	Link    string
	Name    string
	Jobs    []prJobData
	Commits []commitData
}

type prJobData struct {
	Name   string
	Link   string
	Builds []buildData
}

type jobBuilds struct {
	name          string
	buildPrefixes []string
}

type commitData struct {
	Hash       string
	HashPrefix string // used only for display purposes, so don't worry about uniqueness
	Link       string
	MaxWidth   int
	latest     time.Time // time stamp of the job most recently started
}

type latestCommit []commitData

func (a latestCommit) Len() int      { return len(a) }
func (a latestCommit) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a latestCommit) Less(i, j int) bool {
	if len(a[i].Hash) != 40 {
		return true
	}
	if len(a[j].Hash) != 40 {
		return false
	}
	return a[i].latest.Before(a[j].latest)
}

type byStarted []buildData

func (a byStarted) Len() int           { return len(a) }
func (a byStarted) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byStarted) Less(i, j int) bool { return a[i].Started.Before(a[j].Started) }

func githubPRLink(githubHost, org, repo string, pr int) string {
	return fmt.Sprintf("https://%s/%s/%s/pull/%d", githubHost, org, repo, pr)
}

func githubCommitLink(githubHost, org, repo, commitHash string) string {
	return fmt.Sprintf("https://%s/%s/%s/commit/%s", githubHost, org, repo, commitHash)
}

func jobHistLink(storageProvider, bucketName, jobName string) string {
	return fmt.Sprintf("/job-history/%s/%s/pr-logs/directory/%s", storageProvider, bucketName, jobName)
}

// gets the pull commit hash from metadata
func getPullCommitHash(pull string) (string, error) {
	match := pullCommitRe.FindStringSubmatch(pull)
	if len(match) != 2 {
		expected := "branch:hash,pullNumber:hash"
		return "", fmt.Errorf("unable to parse pull %q (expected %q)", pull, expected)
	}
	return match[1], nil
}

// listJobBuilds concurrently lists builds for the given job prefixes that have been run on a PR
func listJobBuilds(ctx context.Context, bucket storageBucket, jobPrefixes []string) []jobBuilds {
	jobch := make(chan jobBuilds)
	defer close(jobch)
	for i, jobPrefix := range jobPrefixes {
		go func(i int, jobPrefix string) {
			buildPrefixes, err := bucket.listSubDirs(ctx, jobPrefix)
			if err != nil {
				logrus.WithError(err).Warningf("Error getting builds for job %s", jobPrefix)
			}
			jobch <- jobBuilds{
				name:          path.Base(jobPrefix),
				buildPrefixes: buildPrefixes,
			}
		}(i, jobPrefix)
	}
	jobs := []jobBuilds{}
	for range jobPrefixes {
		job := <-jobch
		jobs = append(jobs, job)
	}
	return jobs
}

// getPRBuildData concurrently fetches metadata on each build of each job run on a PR
func getPRBuildData(ctx context.Context, bucket storageBucket, jobs []jobBuilds) []buildData {
	buildch := make(chan buildData)
	defer close(buildch)
	expected := 0
	for _, job := range jobs {
		for j, buildPrefix := range job.buildPrefixes {
			go func(j int, jobName, buildPrefix string) {
				build, err := getBuildData(ctx, bucket, buildPrefix)
				if err != nil {
					logrus.WithError(err).Warningf("build %s information incomplete", buildPrefix)
				}
				split := strings.Split(strings.TrimSuffix(buildPrefix, "/"), "/")
				build.SpyglassLink = path.Join(spyglassPrefix, bucket.getStorageProvider(), bucket.getName(), buildPrefix)
				build.ID = split[len(split)-1]
				build.jobName = jobName
				build.prefix = buildPrefix
				build.index = j
				buildch <- build
			}(j, job.name, buildPrefix)
			expected++
		}
	}
	builds := []buildData{}
	for k := 0; k < expected; k++ {
		build := <-buildch
		builds = append(builds, build)
	}
	return builds
}

func updateCommitData(commits map[string]*commitData, githubHost, org, repo, hash string, buildTime time.Time, width int) {
	commit, ok := commits[hash]
	if !ok {
		commits[hash] = &commitData{
			Hash:       hash,
			HashPrefix: hash,
		}
		commit = commits[hash]
		if len(hash) == 40 {
			commit.HashPrefix = hash[:7]
			commit.Link = githubCommitLink(githubHost, org, repo, hash)
		}
	}
	if buildTime.After(commit.latest) {
		commit.latest = buildTime
	}
	if width > commit.MaxWidth {
		commit.MaxWidth = width
	}
}

// parsePullURL parses PR history URLs. Expects this format:
// .../pr-history?org=<org>&repo=<repo>&pr=<pr number>
func parsePullURL(u *url.URL) (org, repo string, pr int, err error) {
	var prStr string
	vals := u.Query()
	if org = vals.Get("org"); org == "" {
		return "", "", 0, fmt.Errorf("no value provided for org")
	}
	if repo = vals.Get("repo"); repo == "" {
		return "", "", 0, fmt.Errorf("no value provided for repo")
	}
	prStr = vals.Get("pr")
	pr, err = strconv.Atoi(prStr)
	if err != nil {
		return "", "", 0, fmt.Errorf("invalid PR number %q: %v", prStr, err)
	}
	return org, repo, pr, nil
}

// getStorageDirsForPR returns a map from bucket names -> set of "directories" containing presubmit data
func getStorageDirsForPR(c *config.Config, gitHubClient deckGitHubClient, gitClient git.ClientFactory, org, repo string, prNumber int) (map[string]sets.String, error) {
	toSearch := make(map[string]sets.String)
	fullRepo := org + "/" + repo

	if c.InRepoConfigEnabled(fullRepo) && gitHubClient == nil {
		return nil, errors.New("inrepoconfig is enabled but no --github-token-path configured on deck")
	}
	prRefGetter := config.NewRefGetterForGitHubPullRequest(gitHubClient, org, repo, prNumber)
	presubmits, err := c.GetPresubmits(gitClient, org+"/"+repo, prRefGetter.BaseSHA, prRefGetter.HeadSHA)
	if err != nil {
		return nil, fmt.Errorf("failed to get Presubmits for pull request %s/%s#%d: %v", org, repo, prNumber, err)
	}
	if len(presubmits) == 0 {
		return toSearch, fmt.Errorf("couldn't find presubmits for %q in config", fullRepo)
	}

	for _, presubmit := range presubmits {
		var gcsConfig *v1.GCSConfiguration
		if presubmit.DecorationConfig != nil && presubmit.DecorationConfig.GCSConfiguration != nil {
			gcsConfig = presubmit.DecorationConfig.GCSConfiguration
		} else {
			// for undecorated jobs assume the default
			gcsConfig = c.Plank.GetDefaultDecorationConfigs(fullRepo).GCSConfiguration
		}

		gcsPath, _, _ := gcsupload.PathsForJob(gcsConfig, &downwardapi.JobSpec{
			Type: v1.PresubmitJob,
			Job:  presubmit.Name,
			Refs: &v1.Refs{
				Repo: repo,
				Org:  org,
				Pulls: []v1.Pull{
					{Number: prNumber},
				},
			},
		}, "")
		gcsPath, _ = path.Split(path.Clean(gcsPath))
		bucketName := gcsConfig.Bucket
		// bucket is the bucket field of the GCSConfiguration, which means it could be missing the
		// storageProvider prefix (but it's deprecated to use a bucket name without <storage-type>:// prefix)
		if !strings.Contains(bucketName, "://") {
			bucketName = "gs://" + bucketName
		}
		if _, ok := toSearch[bucketName]; !ok {
			toSearch[bucketName] = sets.String{}
		}
		toSearch[bucketName].Insert(gcsPath)
	}
	return toSearch, nil
}

func getPRHistory(ctx context.Context, prHistoryURL *url.URL, config *config.Config, opener io.Opener, gitHubClient deckGitHubClient, gitClient git.ClientFactory, githubHost string) (prHistoryTemplate, error) {
	start := time.Now()
	template := prHistoryTemplate{}

	org, repo, pr, err := parsePullURL(prHistoryURL)
	if err != nil {
		return template, fmt.Errorf("failed to parse URL %s: %v", prHistoryURL.String(), err)
	}
	template.Name = fmt.Sprintf("%s/%s #%d", org, repo, pr)
	template.Link = githubPRLink(githubHost, org, repo, pr) // TODO(ibzib) support Gerrit :/

	toSearch, err := getStorageDirsForPR(config, gitHubClient, gitClient, org, repo, pr)
	if err != nil {
		return template, fmt.Errorf("failed to list directories for PR %s: %v", template.Name, err)
	}

	builds := []buildData{}
	// job name -> commit hash -> list of builds
	jobCommitBuilds := make(map[string]map[string][]buildData)

	for bucket, storagePaths := range toSearch {
		parsedBucket, err := url.Parse(bucket)
		if err != nil {
			return template, fmt.Errorf("parse bucket %s: %w", bucket, err)
		}
		bucketName := parsedBucket.Host
		storageProvider := parsedBucket.Scheme
		bucket, err := newBlobStorageBucket(bucketName, storageProvider, config, opener)
		if err != nil {
			return template, err
		}
		for storagePath := range storagePaths {
			jobPrefixes, err := bucket.listSubDirs(ctx, storagePath)
			if err != nil {
				return template, fmt.Errorf("failed to get job names: %v", err)
			}
			// We assume job names to be unique, as enforced during config validation.
			for _, jobPrefix := range jobPrefixes {
				jobName := path.Base(jobPrefix)
				jobData := prJobData{
					Name: jobName,
					Link: jobHistLink(storageProvider, bucketName, jobName),
				}
				template.Jobs = append(template.Jobs, jobData)
				jobCommitBuilds[jobName] = make(map[string][]buildData)
			}
			jobs := listJobBuilds(ctx, bucket, jobPrefixes)
			builds = append(builds, getPRBuildData(ctx, bucket, jobs)...)
		}
	}

	commits := make(map[string]*commitData)
	for _, build := range builds {
		jobName := build.jobName
		hash := build.commitHash
		jobCommitBuilds[jobName][hash] = append(jobCommitBuilds[jobName][hash], build)
		updateCommitData(commits, githubHost, org, repo, hash, build.Started, len(jobCommitBuilds[jobName][hash]))
	}
	for _, commit := range commits {
		template.Commits = append(template.Commits, *commit)
	}
	// builds are grouped by commit, then sorted by build start time (newest-first)
	sort.Sort(sort.Reverse(latestCommit(template.Commits)))
	for i, job := range template.Jobs {
		for _, commit := range template.Commits {
			builds := jobCommitBuilds[job.Name][commit.Hash]
			sort.Sort(sort.Reverse(byStarted(builds)))
			template.Jobs[i].Builds = append(template.Jobs[i].Builds, builds...)
			// pad empty spaces
			for k := len(builds); k < commit.MaxWidth; k++ {
				template.Jobs[i].Builds = append(template.Jobs[i].Builds, buildData{})
			}
		}
	}

	elapsed := time.Since(start)
	logrus.WithField("duration", elapsed.String()).Infof("loaded %s", prHistoryURL.Path)

	return template, nil
}
<commit_msg>Fix cornercase where branches such as Release-1.0 would fail the regexp test<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"path"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/sets"

	v1 "k8s.io/test-infra/prow/apis/prowjobs/v1"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/gcsupload"
	"k8s.io/test-infra/prow/git/v2"
	"k8s.io/test-infra/prow/io"
	"k8s.io/test-infra/prow/pod-utils/downwardapi"
)

var pullCommitRe = regexp.MustCompile(`^[-\.\w]+:\w{40},\d+:(\w{40})$`)

type prHistoryTemplate struct {
	Link    string
	Name    string
	Jobs    []prJobData
	Commits []commitData
}

type prJobData struct {
	Name   string
	Link   string
	Builds []buildData
}

type jobBuilds struct {
	name          string
	buildPrefixes []string
}

type commitData struct {
	Hash       string
	HashPrefix string // used only for display purposes, so don't worry about uniqueness
	Link       string
	MaxWidth   int
	latest     time.Time // time stamp of the job most recently started
}

type latestCommit []commitData

func (a latestCommit) Len() int      { return len(a) }
func (a latestCommit) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a latestCommit) Less(i, j int) bool {
	if len(a[i].Hash) != 40 {
		return true
	}
	if len(a[j].Hash) != 40 {
		return false
	}
	return a[i].latest.Before(a[j].latest)
}

type byStarted []buildData

func (a byStarted) Len() int           { return len(a) }
func (a byStarted) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byStarted) Less(i, j int) bool { return a[i].Started.Before(a[j].Started) }

func githubPRLink(githubHost, org, repo string, pr int) string {
	return fmt.Sprintf("https://%s/%s/%s/pull/%d", githubHost, org, repo, pr)
}

func githubCommitLink(githubHost, org, repo, commitHash string) string {
	return fmt.Sprintf("https://%s/%s/%s/commit/%s", githubHost, org, repo, commitHash)
}

func jobHistLink(storageProvider, bucketName, jobName string) string {
	return fmt.Sprintf("/job-history/%s/%s/pr-logs/directory/%s", storageProvider, bucketName, jobName)
}

// gets the pull commit hash from metadata
func getPullCommitHash(pull string) (string, error) {
	match := pullCommitRe.FindStringSubmatch(pull)
	if len(match) != 2 {
		expected := "branch:hash,pullNumber:hash"
		return "", fmt.Errorf("unable to parse pull %q (expected %q)", pull, expected)
	}
	return match[1], nil
}

// listJobBuilds concurrently lists builds for the given job prefixes that have been run on a PR
func listJobBuilds(ctx context.Context, bucket storageBucket, jobPrefixes []string) []jobBuilds {
	jobch := make(chan jobBuilds)
	defer close(jobch)
	for i, jobPrefix := range jobPrefixes {
		go func(i int, jobPrefix string) {
			buildPrefixes, err := bucket.listSubDirs(ctx, jobPrefix)
			if err != nil {
				logrus.WithError(err).Warningf("Error getting builds for job %s", jobPrefix)
			}
			jobch <- jobBuilds{
				name:          path.Base(jobPrefix),
				buildPrefixes: buildPrefixes,
			}
		}(i, jobPrefix)
	}
	jobs := []jobBuilds{}
	for range jobPrefixes {
		job := <-jobch
		jobs = append(jobs, job)
	}
	return jobs
}

// getPRBuildData concurrently fetches metadata on each build of each job run on a PR
func getPRBuildData(ctx context.Context, bucket storageBucket, jobs []jobBuilds) []buildData {
	buildch := make(chan buildData)
	defer close(buildch)
	expected := 0
	for _, job := range jobs {
		for j, buildPrefix := range job.buildPrefixes {
			go func(j int, jobName, buildPrefix string) {
				build, err := getBuildData(ctx, bucket, buildPrefix)
				if err != nil {
					logrus.WithError(err).Warningf("build %s information incomplete", buildPrefix)
				}
				split := strings.Split(strings.TrimSuffix(buildPrefix, "/"), "/")
				build.SpyglassLink = path.Join(spyglassPrefix, bucket.getStorageProvider(), bucket.getName(), buildPrefix)
				build.ID = split[len(split)-1]
				build.jobName = jobName
				build.prefix = buildPrefix
				build.index = j
				buildch <- build
			}(j, job.name, buildPrefix)
			expected++
		}
	}
	builds := []buildData{}
	for k := 0; k < expected; k++ {
		build := <-buildch
		builds = append(builds, build)
	}
	return builds
}

func updateCommitData(commits map[string]*commitData, githubHost, org, repo, hash string, buildTime time.Time, width int) {
	commit, ok := commits[hash]
	if !ok {
		commits[hash] = &commitData{
			Hash:       hash,
			HashPrefix: hash,
		}
		commit = commits[hash]
		if len(hash) == 40 {
			commit.HashPrefix = hash[:7]
			commit.Link = githubCommitLink(githubHost, org, repo, hash)
		}
	}
	if buildTime.After(commit.latest) {
		commit.latest = buildTime
	}
	if width > commit.MaxWidth {
		commit.MaxWidth = width
	}
}

// parsePullURL parses PR history URLs. Expects this format:
// .../pr-history?org=<org>&repo=<repo>&pr=<pr number>
func parsePullURL(u *url.URL) (org, repo string, pr int, err error) {
	var prStr string
	vals := u.Query()
	if org = vals.Get("org"); org == "" {
		return "", "", 0, fmt.Errorf("no value provided for org")
	}
	if repo = vals.Get("repo"); repo == "" {
		return "", "", 0, fmt.Errorf("no value provided for repo")
	}
	prStr = vals.Get("pr")
	pr, err = strconv.Atoi(prStr)
	if err != nil {
		return "", "", 0, fmt.Errorf("invalid PR number %q: %v", prStr, err)
	}
	return org, repo, pr, nil
}

// getStorageDirsForPR returns a map from bucket names -> set of "directories" containing presubmit data
func getStorageDirsForPR(c *config.Config, gitHubClient deckGitHubClient, gitClient git.ClientFactory, org, repo string, prNumber int) (map[string]sets.String, error) {
	toSearch := make(map[string]sets.String)
	fullRepo := org + "/" + repo

	if c.InRepoConfigEnabled(fullRepo) && gitHubClient == nil {
		return nil, errors.New("inrepoconfig is enabled but no --github-token-path configured on deck")
	}
	prRefGetter := config.NewRefGetterForGitHubPullRequest(gitHubClient, org, repo, prNumber)
	presubmits, err := c.GetPresubmits(gitClient, org+"/"+repo, prRefGetter.BaseSHA, prRefGetter.HeadSHA)
	if err != nil {
		return nil, fmt.Errorf("failed to get Presubmits for pull request %s/%s#%d: %v", org, repo, prNumber, err)
	}
	if len(presubmits) == 0 {
		return toSearch, fmt.Errorf("couldn't find presubmits for %q in config", fullRepo)
	}

	for _, presubmit := range presubmits {
		var gcsConfig *v1.GCSConfiguration
		if presubmit.DecorationConfig != nil && presubmit.DecorationConfig.GCSConfiguration != nil {
			gcsConfig = presubmit.DecorationConfig.GCSConfiguration
		} else {
			// for undecorated jobs assume the default
			gcsConfig = c.Plank.GetDefaultDecorationConfigs(fullRepo).GCSConfiguration
		}

		gcsPath, _, _ := gcsupload.PathsForJob(gcsConfig, &downwardapi.JobSpec{
			Type: v1.PresubmitJob,
			Job:  presubmit.Name,
			Refs: &v1.Refs{
				Repo: repo,
				Org:  org,
				Pulls: []v1.Pull{
					{Number: prNumber},
				},
			},
		}, "")
		gcsPath, _ = path.Split(path.Clean(gcsPath))
		bucketName := gcsConfig.Bucket
		// bucket is the bucket field of the GCSConfiguration, which means it could be missing the
		// storageProvider prefix (but it's deprecated to use a bucket name without <storage-type>:// prefix)
		if !strings.Contains(bucketName, "://") {
			bucketName = "gs://" + bucketName
		}
		if _, ok := toSearch[bucketName]; !ok {
			toSearch[bucketName] = sets.String{}
		}
		toSearch[bucketName].Insert(gcsPath)
	}
	return toSearch, nil
}

func getPRHistory(ctx context.Context, prHistoryURL *url.URL, config *config.Config, opener io.Opener, gitHubClient deckGitHubClient, gitClient git.ClientFactory, githubHost string) (prHistoryTemplate, error) {
	start := time.Now()
	template := prHistoryTemplate{}

	org, repo, pr, err := parsePullURL(prHistoryURL)
	if err != nil {
		return template, fmt.Errorf("failed to parse URL %s: %v", prHistoryURL.String(), err)
	}
	template.Name = fmt.Sprintf("%s/%s #%d", org, repo, pr)
	template.Link = githubPRLink(githubHost, org, repo, pr) // TODO(ibzib) support Gerrit :/

	toSearch, err := getStorageDirsForPR(config, gitHubClient, gitClient, org, repo, pr)
	if err != nil {
		return template, fmt.Errorf("failed to list directories for PR %s: %v", template.Name, err)
	}

	builds := []buildData{}
	// job name -> commit hash -> list of builds
	jobCommitBuilds := make(map[string]map[string][]buildData)

	for bucket, storagePaths := range toSearch {
		parsedBucket, err := url.Parse(bucket)
		if err != nil {
			return template, fmt.Errorf("parse bucket %s: %w", bucket, err)
		}
		bucketName := parsedBucket.Host
		storageProvider := parsedBucket.Scheme
		bucket, err := newBlobStorageBucket(bucketName, storageProvider, config, opener)
		if err != nil {
			return template, err
		}
		for storagePath := range storagePaths {
			jobPrefixes, err := bucket.listSubDirs(ctx, storagePath)
			if err != nil {
				return template, fmt.Errorf("failed to get job names: %v", err)
			}
			// We assume job names to be unique, as enforced during config validation.
			for _, jobPrefix := range jobPrefixes {
				jobName := path.Base(jobPrefix)
				jobData := prJobData{
					Name: jobName,
					Link: jobHistLink(storageProvider, bucketName, jobName),
				}
				template.Jobs = append(template.Jobs, jobData)
				jobCommitBuilds[jobName] = make(map[string][]buildData)
			}
			jobs := listJobBuilds(ctx, bucket, jobPrefixes)
			builds = append(builds, getPRBuildData(ctx, bucket, jobs)...)
		}
	}

	commits := make(map[string]*commitData)
	for _, build := range builds {
		jobName := build.jobName
		hash := build.commitHash
		jobCommitBuilds[jobName][hash] = append(jobCommitBuilds[jobName][hash], build)
		updateCommitData(commits, githubHost, org, repo, hash, build.Started, len(jobCommitBuilds[jobName][hash]))
	}
	for _, commit := range commits {
		template.Commits = append(template.Commits, *commit)
	}
	// builds are grouped by commit, then sorted by build start time (newest-first)
	sort.Sort(sort.Reverse(latestCommit(template.Commits)))
	for i, job := range template.Jobs {
		for _, commit := range template.Commits {
			builds := jobCommitBuilds[job.Name][commit.Hash]
			sort.Sort(sort.Reverse(byStarted(builds)))
			template.Jobs[i].Builds = append(template.Jobs[i].Builds, builds...)
			// pad empty spaces
			for k := len(builds); k < commit.MaxWidth; k++ {
				template.Jobs[i].Builds = append(template.Jobs[i].Builds, buildData{})
			}
		}
	}

	elapsed := time.Since(start)
	logrus.WithField("duration", elapsed.String()).Infof("loaded %s", prHistoryURL.Path)

	return template, nil
}
<|endoftext|>


<commit_before>package gen

import (
	//"errors"
	"fmt"
	"reflect"
	"strings"
)

var (
	mssType reflect.Type
	msiType reflect.Type
	btsType reflect.Type
)

func init() {
	var mss map[string]string
	mssType = reflect.TypeOf(mss)
	var msi map[string]interface{}
	msiType = reflect.TypeOf(msi)
	var bts []byte
	btsType = reflect.TypeOf(bts)
}

// This code defines the template
// syntax tree. If the input were:
//
// type Marshaler struct {
// 	thing1 *float64
// 	body   []byte
// }
//
// then the AST parsing of *Marshaler should produce:
//
// var val Elem = &Ptr{
// 	Varname: "z",
// 	Value: &Struct{
// 		Name: "Marshaler",
// 		Fields: []StructField{
// 			{
// 				FieldTag: "thing1",
// 				FieldElem: &Ptr{
// 					Varname: "z.thing1",
// 					Value: &BaseElem{
// 						Varname: "*z.thing1",
// 						Value:   Float64,
// 					},
// 				},
// 			},
// 			{
// 				FieldTag: "",
// 				FieldElem: &BaseElem{
// 					Varname: "z.body",
// 					Value:   Bytes,
// 				},
// 			},
// 		},
// 	},
// }

// Base is one of the
// base types
type Base int

// this is effectively the
// list of currently available
// ReadXxxx / WriteXxxx methods.
const (
	Invalid Base = iota
	Bytes
	String
	Float32
	Float64
	Complex64
	Complex128
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Byte
	Int
	Int8
	Int16
	Int32
	Int64
	Bool
	MapStrStr  // map[string]string
	MapStrIntf // map[string]interface{}
	Intf       // interface{}

	IDENT
)

type ElemType int

const (
	InvalidType ElemType = iota
	PtrType
	SliceType
	StructType
	BaseType
)

// Elem is Base, Slice, Stuct, or Ptr
type Elem interface {
	Type() ElemType // Returns element type
	Ptr() *Ptr
	Slice() *Slice
	Struct() *Struct
	Base() *BaseElem
	TypeName() string
	String() string
}

type Slice struct {
	Varname string
	Els     Elem // The type of each element
}

func (s *Slice) Type() ElemType   { return SliceType }
func (s *Slice) Ptr() *Ptr        { return nil }
func (s *Slice) Slice() *Slice    { return s }
func (s *Slice) Struct() *Struct  { return nil }
func (s *Slice) Base() *BaseElem  { return nil }
func (s *Slice) TypeName() string { return s.Els.TypeName() }
func (s *Slice) String() string {
	return fmt.Sprintf("SliceOf(%s - %s)", s.Els.String(), s.Varname)
}

type Ptr struct {
	Varname string
	Value   Elem
}

func (s *Ptr) Type() ElemType   { return PtrType }
func (s *Ptr) Ptr() *Ptr        { return s }
func (s *Ptr) Slice() *Slice    { return nil }
func (s *Ptr) Struct() *Struct  { return nil }
func (s *Ptr) Base() *BaseElem  { return nil }
func (s *Ptr) TypeName() string { return s.Value.TypeName() }
func (s *Ptr) String() string {
	return fmt.Sprintf("PointerTo(%s - %s)", s.Value.String(), s.Varname)
}

type Struct struct {
	Name   string
	Fields []StructField
}

func (s *Struct) Type() ElemType   { return StructType }
func (s *Struct) Ptr() *Ptr        { return nil }
func (s *Struct) Slice() *Slice    { return nil }
func (s *Struct) Struct() *Struct  { return s }
func (s *Struct) Base() *BaseElem  { return nil }
func (s *Struct) TypeName() string { return s.Name }
func (s *Struct) String() string {
	return fmt.Sprintf("%s{%s}", s.Name, s.Fields)
}

type StructField struct {
	FieldTag  string
	FieldName string
	FieldElem Elem
}

func (s StructField) String() string {
	return fmt.Sprintf("\n\t%s: %s %q, ", s.FieldName, s.FieldElem, s.FieldTag)
}

type BaseElem struct {
	Varname string
	Value   Base
	Ident   string // IDENT name if unresolved
	Convert bool   // should we do an explicit conversion?
}

func (s *BaseElem) Type() ElemType  { return BaseType }
func (s *BaseElem) Ptr() *Ptr       { return nil }
func (s *BaseElem) Slice() *Slice   { return nil }
func (s *BaseElem) Struct() *Struct { return nil }
func (s *BaseElem) Base() *BaseElem { return s }
func (s *BaseElem) String() string  { return fmt.Sprintf("(%s - %s)", s.BaseName(), s.Varname) }

// TypeName returns the syntactically correct Go
// type name for the base element.
func (s *BaseElem) TypeName() string {
	if s.Ident != "" {
		return s.Ident
	}

	switch s.Value {
	case MapStrIntf:
		return "map[string]interface{}"
	case MapStrStr:
		return "map[string]string"
	default:
		return strings.ToLower(s.BaseName())
	}
}

// BaseName returns the string form of the
// base type (e.g. Float64, Ident, MapStrStr, etc)
func (s *BaseElem) BaseName() string { return s.Value.String() }
func (s *BaseElem) BaseType() string {
	switch s.Value {
	case IDENT:
		return s.Ident
	case MapStrStr:
		return "map[string]string"
	case MapStrIntf:
		return "map[string]interface{}"
	case Bytes:
		return "[]byte"
	default:
		return strings.ToLower(s.BaseName())
	}
}

// is this a map?
func (s *BaseElem) IsMap() bool { return (s.Value == MapStrStr || s.Value == MapStrIntf) }

// is this an interface{}
func (s *BaseElem) IsIntf() bool { return s.Value == Intf }

// is this an external identity?
func (s *BaseElem) IsIdent() bool { return s.Value == IDENT }

func (k Base) String() string {
	switch k {
	case String:
		return "String"
	case Bytes:
		return "Bytes"
	case Float32:
		return "Float32"
	case Float64:
		return "Float64"
	case Complex64:
		return "Complex64"
	case Complex128:
		return "Complex128"
	case Uint:
		return "Uint"
	case Uint8:
		return "Uint8"
	case Uint16:
		return "Uint16"
	case Uint32:
		return "Uint32"
	case Uint64:
		return "Uint64"
	case Byte:
		return "Byte"
	case Int:
		return "Int"
	case Int8:
		return "Int8"
	case Int16:
		return "Int16"
	case Int32:
		return "Int32"
	case Int64:
		return "Int64"
	case Bool:
		return "Bool"
	case MapStrStr:
		return "MapStrStr"
	case MapStrIntf:
		return "MapStrIntf"
	case Intf:
		return "Intf"
	case IDENT:
		return "Ident"
	default:
		return "INVALID"
	}
}

// construct make the type tree
// out of a reflect.Type. it doesn't try
// to insert variable names, in most cases. additionally,
// some fields may be nil.
func construct(t reflect.Type) Elem {
	switch t.Kind() {
	case reflect.Struct:
		out := &Struct{
			Name:   t.Name(),
			Fields: make([]StructField, t.NumField()),
		}
		for i := range out.Fields {
			field := t.Field(i)
			tag := field.Tag.Get("msg")
			if tag == "" {
				tag = field.Tag.Get("json")
				if tag == "" {
					tag = field.Name
				}
			}
			out.Fields[i] = StructField{
				FieldName: field.Name,
				FieldTag:  tag,
				FieldElem: construct(field.Type),
			}
		}
		return out

	case reflect.Map:
		if t.Key().Kind() != reflect.String {
			return nil
		}
		switch t.Elem().Kind() {
		case reflect.String:
			return &BaseElem{Value: MapStrStr}
		case reflect.Interface:
			return &BaseElem{Value: MapStrIntf}
		default:
			return nil
		}

	case reflect.String:
		return &BaseElem{Value: String}
	case reflect.Slice, reflect.Array:
		if t.AssignableTo(btsType) {
			return &BaseElem{Value: Bytes}
		} else {
			return &Slice{Els: construct(t.Elem())}
		}
	case reflect.Float32:
		return &BaseElem{Value: Float32}
	case reflect.Float64:
		return &BaseElem{Value: Float64}
	case reflect.Int:
		return &BaseElem{Value: Int}
	case reflect.Int8:
		return &BaseElem{Value: Int8}
	case reflect.Int16:
		return &BaseElem{Value: Int16}
	case reflect.Int32:
		return &BaseElem{Value: Int32}
	case reflect.Int64:
		return &BaseElem{Value: Int64}
	case reflect.Uint:
		return &BaseElem{Value: Uint}
	case reflect.Uint8:
		return &BaseElem{Value: Uint8}
	case reflect.Uint16:
		return &BaseElem{Value: Uint16}
	case reflect.Uint32:
		return &BaseElem{Value: Uint32}
	case reflect.Uint64:
		return &BaseElem{Value: Uint64}
	case reflect.Complex64:
		return &BaseElem{Value: Complex64}
	case reflect.Complex128:
		return &BaseElem{Value: Complex128}
	case reflect.Ptr:
		return &Ptr{Value: construct(t.Elem())}
	default:
		return nil
	}
}

// propNames propogates names through a *Ptr-to-*Struct
func Propogate(p *Ptr, name string) {
	if p == nil || p.Value == nil || p.Value.Struct() == nil {
		panic("propogate called on non-Ptr-to-Struct")
	}
	p.Varname = name
	writeStructFields(p.Value.Struct().Fields, name)
}

func writeStructFields(s []StructField, name string) {
	prefix := name + "."
	for i := range s {
		switch s[i].FieldElem.Type() {

		case BaseType:
			s[i].FieldElem.Base().Varname = prefix + s[i].FieldName

		case StructType:
			writeStructFields(s[i].FieldElem.Struct().Fields, prefix+s[i].FieldName)

		case SliceType:
			s[i].FieldElem.Slice().Varname = prefix + s[i].FieldName
			writeBase(s[i].FieldElem.Slice().Els, s[i].FieldElem.Slice())

		case PtrType:
			s[i].FieldElem.Ptr().Varname = prefix + s[i].FieldName
			writeBase(s[i].FieldElem.Ptr().Value, s[i].FieldElem.Ptr())

		default:
			panic("unrecognized type!")
		}
	}
}

// writeBase recursively writes variable names
// on pointers, slices, and base types.
func writeBase(b Elem, parent Elem) {
	switch parent.Type() {
	case SliceType:
		switch b.Type() {
		case BaseType:
			b.Base().Varname = parent.Slice().Varname + "[i]"
		case SliceType:
			b.Slice().Varname = parent.Slice().Varname + "[i]"
			writeBase(b.Slice().Els, b.Slice())
		case PtrType:
			b.Ptr().Varname = parent.Slice().Varname + "[i]"
			writeBase(b.Ptr().Value, b.Ptr())
		case StructType:
			writeStructFields(b.Struct().Fields, parent.Slice().Varname+"[i]")
		}
	case PtrType:
		switch b.Type() {
		case BaseType:
			if b.Base().IsIdent() {
				// SPECIAL CASE
				// WE ASSUME POINTER IDENTS
				// IMPLEMENT THE DECODER/ENCODER INTERFACES.
				b.Base().Varname = parent.Ptr().Varname
			} else {
				b.Base().Varname = "*" + parent.Ptr().Varname
			}
		case SliceType:
			b.Slice().Varname = "*" + parent.Ptr().Varname
			writeBase(b.Slice().Els, b.Slice())
		case PtrType:
			b.Ptr().Varname = "*" + parent.Ptr().Varname
			writeBase(b.Ptr().Value, b.Ptr())
		case StructType:
			// struct fields are dereferenced automatically
			writeStructFields(b.Struct().Fields, parent.Ptr().Varname)
		}
	}
}

func GenerateTree(v reflect.Type) (Elem, error) {
	var err error
	el := construct(v)
	Propogate(el.Ptr(), "z")
	return el, err
}
<commit_msg>make new Elem type for Map<commit_after>package gen

import (
	//"errors"
	"fmt"
	"reflect"
	"strings"
)

var (
	mssType reflect.Type
	msiType reflect.Type
	btsType reflect.Type
)

func init() {
	var mss map[string]string
	mssType = reflect.TypeOf(mss)
	var msi map[string]interface{}
	msiType = reflect.TypeOf(msi)
	var bts []byte
	btsType = reflect.TypeOf(bts)
}

// This code defines the template
// syntax tree. If the input were:
//
// type Marshaler struct {
// 	thing1 *float64
// 	body   []byte
// }
//
// then the AST parsing of *Marshaler should produce:
//
// var val Elem = &Ptr{
// 	Varname: "z",
// 	Value: &Struct{
// 		Name: "Marshaler",
// 		Fields: []StructField{
// 			{
// 				FieldTag: "thing1",
// 				FieldElem: &Ptr{
// 					Varname: "z.thing1",
// 					Value: &BaseElem{
// 						Varname: "*z.thing1",
// 						Value:   Float64,
// 					},
// 				},
// 			},
// 			{
// 				FieldTag: "",
// 				FieldElem: &BaseElem{
// 					Varname: "z.body",
// 					Value:   Bytes,
// 				},
// 			},
// 		},
// 	},
// }

// Base is one of the
// base types
type Base int

// this is effectively the
// list of currently available
// ReadXxxx / WriteXxxx methods.
const (
	Invalid Base = iota
	Bytes
	String
	Float32
	Float64
	Complex64
	Complex128
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Byte
	Int
	Int8
	Int16
	Int32
	Int64
	Bool
	//MapStrStr  // map[string]string
	//MapStrIntf // map[string]interface{}
	Intf // interface{}

	IDENT
)

type ElemType int

const (
	InvalidType ElemType = iota
	PtrType
	SliceType
	StructType
	BaseType
	MapType
)

// Elem is Base, Slice, Stuct, Map, or Ptr
type Elem interface {
	Type() ElemType // Returns element type
	Ptr() *Ptr
	Slice() *Slice
	Struct() *Struct
	Base() *BaseElem
	Map() *Map
	TypeName() string
	String() string
}

// Map is a map[string]Elem
type Map struct {
	Varname string
	Value   Elem
}

func (m *Map) Type() ElemType   { return MapType }
func (m *Map) Ptr() *Ptr        { return nil }
func (m *Map) Slice() *Slice    { return nil }
func (m *Map) Struct() *Struct  { return nil }
func (m *Map) Base() *BaseElem  { return nil }
func (m *Map) Map() *Map        { return m }
func (m *Map) TypeName() string { return fmt.Sprintf("map[string]%s", m.Value.TypeName()) }
func (m *Map) String() string {
	return fmt.Sprintf("MapOf([string]%s - %s)", m.Value.String(), m.Varname)
}

type Slice struct {
	Varname string
	Els     Elem // The type of each element
}

func (s *Slice) Type() ElemType   { return SliceType }
func (s *Slice) Ptr() *Ptr        { return nil }
func (s *Slice) Slice() *Slice    { return s }
func (s *Slice) Struct() *Struct  { return nil }
func (s *Slice) Base() *BaseElem  { return nil }
func (s *Slice) Map() *Map        { return nil }
func (s *Slice) TypeName() string { return s.Els.TypeName() }
func (s *Slice) String() string {
	return fmt.Sprintf("SliceOf(%s - %s)", s.Els.String(), s.Varname)
}

type Ptr struct {
	Varname string
	Value   Elem
}

func (s *Ptr) Type() ElemType   { return PtrType }
func (s *Ptr) Ptr() *Ptr        { return s }
func (s *Ptr) Slice() *Slice    { return nil }
func (s *Ptr) Struct() *Struct  { return nil }
func (s *Ptr) Base() *BaseElem  { return nil }
func (s *Ptr) Map() *Map        { return nil }
func (s *Ptr) TypeName() string { return s.Value.TypeName() }
func (s *Ptr) String() string {
	return fmt.Sprintf("PointerTo(%s - %s)", s.Value.String(), s.Varname)
}

type Struct struct {
	Name   string
	Fields []StructField
}

func (s *Struct) Type() ElemType   { return StructType }
func (s *Struct) Ptr() *Ptr        { return nil }
func (s *Struct) Slice() *Slice    { return nil }
func (s *Struct) Struct() *Struct  { return s }
func (s *Struct) Base() *BaseElem  { return nil }
func (s *Struct) Map() *Map        { return nil }
func (s *Struct) TypeName() string { return s.Name }
func (s *Struct) String() string {
	return fmt.Sprintf("%s{%s}", s.Name, s.Fields)
}

type StructField struct {
	FieldTag  string
	FieldName string
	FieldElem Elem
}

func (s StructField) String() string {
	return fmt.Sprintf("\n\t%s: %s %q, ", s.FieldName, s.FieldElem, s.FieldTag)
}

type BaseElem struct {
	Varname string
	Value   Base
	Ident   string // IDENT name if unresolved
	Convert bool   // should we do an explicit conversion?
}

func (s *BaseElem) Type() ElemType  { return BaseType }
func (s *BaseElem) Ptr() *Ptr       { return nil }
func (s *BaseElem) Slice() *Slice   { return nil }
func (s *BaseElem) Struct() *Struct { return nil }
func (s *BaseElem) Map() *Map       { return nil }
func (s *BaseElem) Base() *BaseElem { return s }
func (s *BaseElem) String() string  { return fmt.Sprintf("(%s - %s)", s.BaseName(), s.Varname) }

// TypeName returns the syntactically correct Go
// type name for the base element.
func (s *BaseElem) TypeName() string {
	if s.Ident != "" {
		return s.Ident
	}
	return s.BaseType()
}

// BaseName returns the string form of the
// base type (e.g. Float64, Ident, MapStrStr, etc)
func (s *BaseElem) BaseName() string { return s.Value.String() }
func (s *BaseElem) BaseType() string {
	switch s.Value {
	case IDENT:
		return s.Ident
	case Intf:
		return "interface{}"
	case Bytes:
		return "[]byte"
	default:
		return strings.ToLower(s.BaseName())
	}
}

// is this an interface{}
func (s *BaseElem) IsIntf() bool { return s.Value == Intf }

// is this an external identity?
func (s *BaseElem) IsIdent() bool { return s.Value == IDENT }

func (k Base) String() string {
	switch k {
	case String:
		return "String"
	case Bytes:
		return "Bytes"
	case Float32:
		return "Float32"
	case Float64:
		return "Float64"
	case Complex64:
		return "Complex64"
	case Complex128:
		return "Complex128"
	case Uint:
		return "Uint"
	case Uint8:
		return "Uint8"
	case Uint16:
		return "Uint16"
	case Uint32:
		return "Uint32"
	case Uint64:
		return "Uint64"
	case Byte:
		return "Byte"
	case Int:
		return "Int"
	case Int8:
		return "Int8"
	case Int16:
		return "Int16"
	case Int32:
		return "Int32"
	case Int64:
		return "Int64"
	case Bool:
		return "Bool"
	case Intf:
		return "Intf"
	case IDENT:
		return "Ident"
	default:
		return "INVALID"
	}
}

// construct make the type tree
// out of a reflect.Type. it doesn't try
// to insert variable names, in most cases. additionally,
// some fields may be nil.
/*
func construct(t reflect.Type) Elem {
	switch t.Kind() {
	case reflect.Struct:
		out := &Struct{
			Name:   t.Name(),
			Fields: make([]StructField, t.NumField()),
		}
		for i := range out.Fields {
			field := t.Field(i)
			tag := field.Tag.Get("msg")
			if tag == "" {
				tag = field.Tag.Get("json")
				if tag == "" {
					tag = field.Name
				}
			}
			out.Fields[i] = StructField{
				FieldName: field.Name,
				FieldTag:  tag,
				FieldElem: construct(field.Type),
			}
		}
		return out

	case reflect.Map:
		if t.Key().Kind() != reflect.String {
			return nil
		}
		switch t.Elem().Kind() {
		case reflect.String:
			return &BaseElem{Value: MapStrStr}
		case reflect.Interface:
			return &BaseElem{Value: MapStrIntf}
		default:
			return nil
		}

	case reflect.String:
		return &BaseElem{Value: String}
	case reflect.Slice, reflect.Array:
		if t.AssignableTo(btsType) {
			return &BaseElem{Value: Bytes}
		} else {
			return &Slice{Els: construct(t.Elem())}
		}
	case reflect.Float32:
		return &BaseElem{Value: Float32}
	case reflect.Float64:
		return &BaseElem{Value: Float64}
	case reflect.Int:
		return &BaseElem{Value: Int}
	case reflect.Int8:
		return &BaseElem{Value: Int8}
	case reflect.Int16:
		return &BaseElem{Value: Int16}
	case reflect.Int32:
		return &BaseElem{Value: Int32}
	case reflect.Int64:
		return &BaseElem{Value: Int64}
	case reflect.Uint:
		return &BaseElem{Value: Uint}
	case reflect.Uint8:
		return &BaseElem{Value: Uint8}
	case reflect.Uint16:
		return &BaseElem{Value: Uint16}
	case reflect.Uint32:
		return &BaseElem{Value: Uint32}
	case reflect.Uint64:
		return &BaseElem{Value: Uint64}
	case reflect.Complex64:
		return &BaseElem{Value: Complex64}
	case reflect.Complex128:
		return &BaseElem{Value: Complex128}
	case reflect.Ptr:
		return &Ptr{Value: construct(t.Elem())}
	default:
		return nil
	}
}
*/

// propNames propogates names through a *Ptr-to-*Struct
func Propogate(p *Ptr, name string) {
	if p == nil || p.Value == nil || p.Value.Struct() == nil {
		panic("propogate called on non-Ptr-to-Struct")
	}
	p.Varname = name
	writeStructFields(p.Value.Struct().Fields, name)
}

// writeStructFields is a trampoline for writeBase for
// all of the fields in a struct
func writeStructFields(s []StructField, name string) {
	prefix := name + "."
	for i := range s {
		switch s[i].FieldElem.Type() {

		case BaseType:
			s[i].FieldElem.Base().Varname = prefix + s[i].FieldName

		case StructType:
			writeStructFields(s[i].FieldElem.Struct().Fields, prefix+s[i].FieldName)

		case SliceType:
			s[i].FieldElem.Slice().Varname = prefix + s[i].FieldName
			writeBase(s[i].FieldElem.Slice().Els, s[i].FieldElem.Slice())

		case PtrType:
			s[i].FieldElem.Ptr().Varname = prefix + s[i].FieldName
			writeBase(s[i].FieldElem.Ptr().Value, s[i].FieldElem.Ptr())

		case MapType:
			s[i].FieldElem.Map().Varname = prefix + s[i].FieldName
			writeBase(s[i].FieldElem.Map().Value, s[i].FieldElem.Map())

		default:
			panic("unrecognized type!")
		}
	}
}

// writeBase recursively writes variable names
// on pointers, slices, and base types, using the name
// of the parent to name the child
func writeBase(b Elem, parent Elem) {
	switch parent.Type() {
	case SliceType:
		switch b.Type() {
		case BaseType:
			b.Base().Varname = parent.Slice().Varname + "[i]"
		case SliceType:
			b.Slice().Varname = parent.Slice().Varname + "[i]"
			writeBase(b.Slice().Els, b.Slice())
		case PtrType:
			b.Ptr().Varname = parent.Slice().Varname + "[i]"
			writeBase(b.Ptr().Value, b.Ptr())
		case StructType:
			writeStructFields(b.Struct().Fields, parent.Slice().Varname+"[i]")
		case MapType:
			b.Map().Varname = parent.Slice().Varname + "[i]"
			writeBase(b.Map().Value, b.Map())
		}
	case PtrType:
		switch b.Type() {
		case BaseType:
			if b.Base().IsIdent() {
				// SPECIAL CASE
				// WE ASSUME POINTER IDENTS
				// IMPLEMENT THE DECODER/ENCODER INTERFACES.
				b.Base().Varname = parent.Ptr().Varname
			} else {
				b.Base().Varname = "*" + parent.Ptr().Varname
			}
		case SliceType:
			b.Slice().Varname = "*" + parent.Ptr().Varname
			writeBase(b.Slice().Els, b.Slice())
		case PtrType:
			b.Ptr().Varname = "*" + parent.Ptr().Varname
			writeBase(b.Ptr().Value, b.Ptr())
		case MapType:
			b.Map().Varname = "*" + parent.Ptr().Varname
			writeBase(b.Map().Value, b.Map())
		case StructType:
			// struct fields are dereferenced automatically
			writeStructFields(b.Struct().Fields, parent.Ptr().Varname)
		}

	case MapType:
		switch b.Type() {
		case BaseType:
			b.Base().Varname = "val"
		case PtrType:
			b.Ptr().Varname = "val"
			writeBase(b.Ptr().Value, b.Ptr())
		case SliceType:
			b.Slice().Varname = "val"
			writeBase(b.Slice().Els, b.Slice())
		case MapType:
			b.Map().Varname = "val"
			writeBase(b.Map().Value, b.Map())
		case StructType:
			writeStructFields(b.Struct().Fields, "val")
		}
	}
}

/*
func GenerateTree(v reflect.Type) (Elem, error) {
	var err error
	el := construct(v)
	Propogate(el.Ptr(), "z")
	return el, err
}*/
<|endoftext|>