<commit_before>package main

import (
	"encoding/json"
	"errors"
	"github.com/franela/goreq"
	"github.com/gin-gonic/gin"
	"log"
	"sync/atomic"
	"time"
)

var errTaskNotFresh = errors.New("This task has been running too long to request a token.")
var errAlreadyGivenKey = errors.New("This task has already been given a token.")
var usedTaskIds = NewTtlSet()

func createToken(token string, opts interface{}) (string, error) {
	r, err := goreq.Request{
		Uri:    vaultPath("/v1/auth/token/create", ""),
		Method: "POST",
		Body:   opts,
	}.WithHeader("X-Vault-Token", token).Do()
	if err == nil {
		defer r.Body.Close()
		switch r.StatusCode {
		case 200:
			var t vaultTokenResp
			if err := r.Body.FromJsonTo(&t); err == nil {
				return t.Auth.ClientToken, nil
			} else {
				return "", err
			}
		default:
			var e vaultError
			e.Code = r.StatusCode
			if err := r.Body.FromJsonTo(&e); err == nil {
				return "", e
			} else {
				e.Errors = []string{"communication error."}
				return "", e
			}
		}
	} else {
		return "", err
	}
}

func createTokenPair(token string, p *policy) (string, error) {
	tempTokenOpts := struct {
		Ttl      string   `json:"ttl"`
		NumUses  int      `json:"num_uses"`
		Policies []string `json:"policies"`
		NoParent bool     `json:"no_parent"`
	}{"10m", 2, []string{"default"}, true}
	pol := p.Policies
	if len(pol) == 0 { // explicitly set the policy, else the token will inherit ours
		pol = []string{"default"}
	}
	permTokenOpts := struct {
		Ttl      string            `json:"ttl,omitempty"`
		Policies []string          `json:"policies"`
		Meta     map[string]string `json:"meta,omitempty"`
		NumUses  int               `json:"num_uses"`
		NoParent bool              `json:"no_parent"`
	}{time.Duration(time.Duration(p.Ttl) * time.Second).String(), pol, p.Meta, p.NumUses, true}

	if tempToken, err := createToken(token, tempTokenOpts); err == nil {
		if permToken, err := createToken(token, permTokenOpts); err == nil {
			r, err := goreq.Request{
				Uri:    vaultPath("/v1/cubbyhole/vault-token", ""),
				Method: "POST",
				Body: struct {
					Token string `json:"token"`
				}{permToken},
			}.WithHeader("X-Vault-Token", tempToken).Do()
			if err == nil {
				defer r.Body.Close()
				switch r.StatusCode {
				case 204:
					return tempToken, nil
				default:
					var e vaultError
					e.Code = r.StatusCode
					if err := r.Body.FromJsonTo(&e); err == nil {
						return "", e
					} else {
						e.Errors = []string{"communication error."}
						return "", e
					}
				}
			} else {
				return "", err
			}
		} else {
			return "", err
		}
	} else {
		return "", err
	}
}

func Provide(c *gin.Context) {
	state.RLock()
	status := state.Status
	token := state.Token
	state.RUnlock()

	remoteIp := c.Request.RemoteAddr

	atomic.AddInt32(&state.Stats.Requests, 1)

	if status == StatusSealed {
		log.Printf("Rejected token request from %s. Reason: sealed.", remoteIp)
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(503, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, "Gatekeeper is sealed."})
		return
	}

	var reqParams struct {
		TaskId string `json:"task_id"`
	}
	decoder := json.NewDecoder(c.Request.Body)
	if err := decoder.Decode(&reqParams); err == nil {
		if usedTaskIds.Has(reqParams.TaskId) {
			log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, errAlreadyGivenKey)
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, errAlreadyGivenKey.Error()})
			return
		}
		/*
			Sometimes the mesos task status isn't available yet in mesos
			when we are asked for a token. In this case we wait a little while
			and then try to get the task info again.
		*/
		gMT := func(taskId string) (mesosTask, error) {
			task, err := getMesosTask(taskId)
			if err == nil && len(task.Statuses) == 0 {
				time.Sleep(500 * time.Millisecond)
				task, err = getMesosTask(taskId)
			}
			return task, err
		}
		if task, err := gMT(reqParams.TaskId); err == nil {
			if len(task.Statuses) == 0 {
				log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v (no status)", remoteIp, reqParams.TaskId, errTaskNotFresh)
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(403, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, errTaskNotFresh.Error()})
				return
			}
			startTime := time.Unix(0, int64(task.Statuses[len(task.Statuses)-1].Timestamp*1000000000))
			if time.Now().Sub(startTime) > config.MaxTaskLife {
				log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v (no status)", remoteIp, reqParams.TaskId, errTaskNotFresh)
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(403, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, errTaskNotFresh.Error()})
				return
			}
			state.RLock()
			policy := activePolicies.Get(task.Name)
			state.RUnlock()
			if tempToken, err := createTokenPair(token, policy); err == nil {
				log.Printf("Provided token pair for %s (Task Id: %s) (Task Name: %s). Policies: %v", remoteIp, reqParams.TaskId, task.Name, policy.Policies)
				atomic.AddInt32(&state.Stats.Successful, 1)
				usedTaskIds.Put(reqParams.TaskId, config.MaxTaskLife+1*time.Minute)
				c.JSON(200, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Token  string `json:"token"`
				}{string(state.Status), true, tempToken})
			} else {
				log.Printf("Failed to create token pair for %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, errTaskNotFresh)
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(500, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, err.Error()})
			}
		} else if err == errNoSuchTask {
			log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, errNoSuchTask)
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		} else {
			log.Printf("Failed to retrieve task information for %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, err)
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(500, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		}
	} else {
		log.Printf("Rejected token request from %s. Reason: %v", remoteIp, err)
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(400, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, err.Error()})
	}
}
<commit_msg>Increase timeout to 1 second.<commit_after>package main

import (
	"encoding/json"
	"errors"
	"github.com/franela/goreq"
	"github.com/gin-gonic/gin"
	"log"
	"sync/atomic"
	"time"
)

var errTaskNotFresh = errors.New("This task has been running too long to request a token.")
var errAlreadyGivenKey = errors.New("This task has already been given a token.")
var usedTaskIds = NewTtlSet()

func createToken(token string, opts interface{}) (string, error) {
	r, err := goreq.Request{
		Uri:    vaultPath("/v1/auth/token/create", ""),
		Method: "POST",
		Body:   opts,
	}.WithHeader("X-Vault-Token", token).Do()
	if err == nil {
		defer r.Body.Close()
		switch r.StatusCode {
		case 200:
			var t vaultTokenResp
			if err := r.Body.FromJsonTo(&t); err == nil {
				return t.Auth.ClientToken, nil
			} else {
				return "", err
			}
		default:
			var e vaultError
			e.Code = r.StatusCode
			if err := r.Body.FromJsonTo(&e); err == nil {
				return "", e
			} else {
				e.Errors = []string{"communication error."}
				return "", e
			}
		}
	} else {
		return "", err
	}
}

func createTokenPair(token string, p *policy) (string, error) {
	tempTokenOpts := struct {
		Ttl      string   `json:"ttl"`
		NumUses  int      `json:"num_uses"`
		Policies []string `json:"policies"`
		NoParent bool     `json:"no_parent"`
	}{"10m", 2, []string{"default"}, true}
	pol := p.Policies
	if len(pol) == 0 { // explicitly set the policy, else the token will inherit ours
		pol = []string{"default"}
	}
	permTokenOpts := struct {
		Ttl      string            `json:"ttl,omitempty"`
		Policies []string          `json:"policies"`
		Meta     map[string]string `json:"meta,omitempty"`
		NumUses  int               `json:"num_uses"`
		NoParent bool              `json:"no_parent"`
	}{time.Duration(time.Duration(p.Ttl) * time.Second).String(), pol, p.Meta, p.NumUses, true}

	if tempToken, err := createToken(token, tempTokenOpts); err == nil {
		if permToken, err := createToken(token, permTokenOpts); err == nil {
			r, err := goreq.Request{
				Uri:    vaultPath("/v1/cubbyhole/vault-token", ""),
				Method: "POST",
				Body: struct {
					Token string `json:"token"`
				}{permToken},
			}.WithHeader("X-Vault-Token", tempToken).Do()
			if err == nil {
				defer r.Body.Close()
				switch r.StatusCode {
				case 204:
					return tempToken, nil
				default:
					var e vaultError
					e.Code = r.StatusCode
					if err := r.Body.FromJsonTo(&e); err == nil {
						return "", e
					} else {
						e.Errors = []string{"communication error."}
						return "", e
					}
				}
			} else {
				return "", err
			}
		} else {
			return "", err
		}
	} else {
		return "", err
	}
}

func Provide(c *gin.Context) {
	state.RLock()
	status := state.Status
	token := state.Token
	state.RUnlock()

	remoteIp := c.Request.RemoteAddr

	atomic.AddInt32(&state.Stats.Requests, 1)

	if status == StatusSealed {
		log.Printf("Rejected token request from %s. Reason: sealed.", remoteIp)
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(503, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, "Gatekeeper is sealed."})
		return
	}

	var reqParams struct {
		TaskId string `json:"task_id"`
	}
	decoder := json.NewDecoder(c.Request.Body)
	if err := decoder.Decode(&reqParams); err == nil {
		if usedTaskIds.Has(reqParams.TaskId) {
			log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, errAlreadyGivenKey)
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, errAlreadyGivenKey.Error()})
			return
		}
		/*
			Sometimes the mesos task status isn't available yet in mesos
			when we are asked for a token. In this case we wait a little while
			and then try to get the task info again.
		*/
		gMT := func(taskId string) (mesosTask, error) {
			task, err := getMesosTask(taskId)
			if err == nil && len(task.Statuses) == 0 {
				time.Sleep(1000 * time.Millisecond)
				task, err = getMesosTask(taskId)
			}
			return task, err
		}
		if task, err := gMT(reqParams.TaskId); err == nil {
			if len(task.Statuses) == 0 {
				log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v (no status)", remoteIp, reqParams.TaskId, errTaskNotFresh)
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(403, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, errTaskNotFresh.Error()})
				return
			}
			startTime := time.Unix(0, int64(task.Statuses[len(task.Statuses)-1].Timestamp*1000000000))
			if time.Now().Sub(startTime) > config.MaxTaskLife {
				log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v (no status)", remoteIp, reqParams.TaskId, errTaskNotFresh)
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(403, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, errTaskNotFresh.Error()})
				return
			}
			state.RLock()
			policy := activePolicies.Get(task.Name)
			state.RUnlock()
			if tempToken, err := createTokenPair(token, policy); err == nil {
				log.Printf("Provided token pair for %s (Task Id: %s) (Task Name: %s). Policies: %v", remoteIp, reqParams.TaskId, task.Name, policy.Policies)
				atomic.AddInt32(&state.Stats.Successful, 1)
				usedTaskIds.Put(reqParams.TaskId, config.MaxTaskLife+1*time.Minute)
				c.JSON(200, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Token  string `json:"token"`
				}{string(state.Status), true, tempToken})
			} else {
				log.Printf("Failed to create token pair for %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, errTaskNotFresh)
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(500, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, err.Error()})
			}
		} else if err == errNoSuchTask {
			log.Printf("Rejected token request from %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, errNoSuchTask)
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		} else {
			log.Printf("Failed to retrieve task information for %s (Task Id: %s). Reason: %v", remoteIp, reqParams.TaskId, err)
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(500, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		}
	} else {
		log.Printf("Rejected token request from %s. Reason: %v", remoteIp, err)
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(400, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, err.Error()})
	}
}
<|endoftext|>


<commit_before>package test

import (
	"flag"
	"sync"
	"testing"

	"github.com/neptulon/neptulon"
	"github.com/neptulon/neptulon/middleware"
)

var ext = flag.Bool("ext", false, "Run external client test case.")

// Helper method for testing client implementations in other languages.
// Flow of events for this function is:
// * Send a {"method":"echo", "params":{"message": "..."}} request to client upon connection,
//   and verify that message body is echoed properly in the response body.
// * Echo any incoming request message body as is within a response message.
// * Repeat ad infinitum, until {"method":"close", "params":"{"message": "..."}"} is received. Close message body is logged.
func TestExternalClient(t *testing.T) {
	sh := NewServerHelper(t).Start()
	defer sh.CloseWait()
	var wg sync.WaitGroup
	wg.Add(3) // one for response handler below, other two for "close" request client-server interaction

	m := "Hello!"

	// send 'echo' request to client upon connection (blocks test if no response is received)
	sh.Server.ConnHandler(func(c *neptulon.Conn) error {
		c.SendRequest("echo", echoMsg{Message: m}, func(ctx *neptulon.ResCtx) error {
			defer wg.Done()
			var msg echoMsg
			if err := ctx.Result(&msg); err != nil {
				t.Fatal(err)
			}
			if msg.Message != m {
				t.Fatalf("server: expected: %v got: %v", m, msg.Message)
			}
			t.Logf("server: client sent 'echo' response message: %v", msg.Message)
			return nil
		})
		return nil
	})

	// handle 'echo' requests via the 'echo middleware'
	srout := middleware.NewRouter()
	sh.Middleware(srout.Middleware)
	srout.Request("echo", middleware.Echo)

	// handle 'close' request (blocks test if no response is received)
	srout.Request("close", func(ctx *neptulon.ReqCtx) error {
		defer wg.Done()
		if err := ctx.Params(&ctx.Res); err != nil {
			return err
		}
		err := ctx.Next()
		ctx.Conn.Close()
		t.Logf("server: closed connection with message from client: %v\n", ctx.Res)
		return err
	})

	if *ext {
		t.Log("Starter server waiting for external client integration test since.")
		wg.Wait()
		return
	}

	// use internal conn implementation instead to test the test case itself
	t.Log("Skipping external client integration test since -ext flag is not provided.")
	ch := sh.GetConnHelper().Connect()
	defer ch.CloseWait()
	cm := "Thanks for echoing! Over and out."

	// handle 'echo' requests via the 'echo middleware'
	crout := middleware.NewRouter()
	ch.Middleware(crout.Middleware)
	crout.Request("echo", middleware.Echo)

	// handle 'echo' request and send 'close' request upon echo response
	ch.SendRequest("echo", echoMsg{Message: m}, func(ctx *neptulon.ResCtx) error {
		var msg echoMsg
		if err := ctx.Result(&msg); err != nil {
			t.Fatal(err)
		}
		if msg.Message != m {
			t.Fatalf("client: expected: %v got: %v", m, msg.Message)
		}
		t.Log("client: server accepted and echoed 'echo' request message body")

		// send close request after getting our echo message back
		ch.SendRequest("close", echoMsg{Message: cm}, func(ctx *neptulon.ResCtx) error {
			wg.Done()
			var msg echoMsg
			if err := ctx.Result(&msg); err != nil {
				t.Fatal(err)
			}
			if msg.Message != cm {
				t.Fatalf("client: expected: %v got: %v", cm, msg.Message)
			}
			t.Log("client: server accepted and echoed 'close' request message body. bye!")
			return nil
		})

		return nil
	})

	wg.Wait()
}
<commit_msg>remove erroneous dupe echo middleware<commit_after>package test

import (
	"flag"
	"sync"
	"testing"

	"github.com/neptulon/neptulon"
	"github.com/neptulon/neptulon/middleware"
)

var ext = flag.Bool("ext", false, "Run external client test case.")

// Helper method for testing client implementations in other languages.
// Flow of events for this function is:
// * Send a {"method":"echo", "params":{"message": "..."}} request to client upon connection,
//   and verify that message body is echoed properly in the response body.
// * Echo any incoming request message body as is within a response message.
// * Repeat ad infinitum, until {"method":"close", "params":"{"message": "..."}"} is received. Close message body is logged.
func TestExternalClient(t *testing.T) {
	sh := NewServerHelper(t).Start()
	defer sh.CloseWait()
	var wg sync.WaitGroup

	m := "Hello!"

	// send 'echo' request to client upon connection (blocks test if no response is received)
	wg.Add(1)
	sh.Server.ConnHandler(func(c *neptulon.Conn) error {
		c.SendRequest("echo", echoMsg{Message: m}, func(ctx *neptulon.ResCtx) error {
			defer wg.Done()
			var msg echoMsg
			if err := ctx.Result(&msg); err != nil {
				t.Fatal(err)
			}
			if msg.Message != m {
				t.Fatalf("server: expected: %v got: %v", m, msg.Message)
			}
			t.Logf("server: client sent 'echo' response message: %v", msg.Message)
			return nil
		})
		return nil
	})

	// handle 'echo' requests via the 'echo middleware'
	srout := middleware.NewRouter()
	srout.Request("echo", middleware.Echo)

	// handle 'close' request (blocks test if no response is received)
	wg.Add(1)
	srout.Request("close", func(ctx *neptulon.ReqCtx) error {
		defer wg.Done()
		if err := ctx.Params(&ctx.Res); err != nil {
			return err
		}
		err := ctx.Next()
		ctx.Conn.Close()
		t.Logf("server: closed connection with message from client: %v\n", ctx.Res)
		return err
	})

	if *ext {
		t.Log("Starter server waiting for external client integration test since.")
		wg.Wait()
		return
	}

	// use internal conn implementation instead to test the test case itself
	t.Log("Skipping external client integration test since -ext flag is not provided.")
	ch := sh.GetConnHelper().Connect()
	defer ch.CloseWait()
	cm := "Thanks for echoing! Over and out."

	// handle 'echo' requests via the 'echo middleware'
	crout := middleware.NewRouter()
	crout.Request("echo", middleware.Echo)

	// handle 'echo' request and send 'close' request upon echo response
	wg.Add(1)
	ch.SendRequest("echo", echoMsg{Message: m}, func(ctx *neptulon.ResCtx) error {
		defer wg.Done()
		var msg echoMsg
		if err := ctx.Result(&msg); err != nil {
			t.Fatal(err)
		}
		if msg.Message != m {
			t.Fatalf("client: expected: %v got: %v", m, msg.Message)
		}
		t.Log("client: server accepted and echoed 'echo' request message body")

		// send close request after getting our echo message back
		wg.Add(1)
		ch.SendRequest("close", echoMsg{Message: cm}, func(ctx *neptulon.ResCtx) error {
			defer wg.Done()
			var msg echoMsg
			if err := ctx.Result(&msg); err != nil {
				t.Fatal(err)
			}
			if msg.Message != cm {
				t.Fatalf("client: expected: %v got: %v", cm, msg.Message)
			}
			t.Log("client: server accepted and echoed 'close' request message body. bye!")
			return nil
		})

		return nil
	})

	wg.Wait()
}
<|endoftext|>


<commit_before>// Range object

package py

// A python Range object
// FIXME one day support BigInts too!
type Range struct {
	Start Int
	Stop  Int
	Step  Int
	//Length Object
}

// A python Range iterator
type RangeIterator struct {
	Range
	Index Int
}

var RangeType = NewTypeX("range", `range(stop) -> range object
range(start, stop[, step]) -> range object

Return a virtual sequence of numbers from start to stop by step.`,
	RangeNew, nil)

var RangeIteratorType = NewType("range_iterator", `range_iterator object`)

// Type of this object
func (o *Range) Type() *Type {
	return RangeType
}

// Type of this object
func (o *RangeIterator) Type() *Type {
	return RangeIteratorType
}

// RangeNew
func RangeNew(metatype *Type, args Tuple, kwargs StringDict) Object {
	var start Object
	var stop Object
	var step Object = Int(1)
	UnpackTuple(args, kwargs, "range", 1, 3, &start, &stop, &step)
	if len(args) == 1 {
		return &Range{
			Start: Int(0),
			Stop:  Index(start),
			Step:  Int(1),
		}
	}
	return &Range{
		Start: Index(start),
		Stop:  Index(stop),
		Step:  Index(step),
	}
}

// Make a range iterator from a range
func (r *Range) M__iter__() Object {
	return &RangeIterator{
		Range: *r,
		Index: r.Start,
	}
}

// Range iterator
func (it *RangeIterator) M__iter__() Object {
	return it
}

// Range iterator next
func (it *RangeIterator) M__next__() Object {
	r := it.Index
	if r >= it.Stop {
		panic(StopIteration)
	}
	it.Index++
	return r
}

// Check interface is satisfied
var _ I__iter__ = (*Range)(nil)
var _ I_iterator = (*RangeIterator)(nil)
<commit_msg>Fix range object to step correctly<commit_after>// Range object

package py

// A python Range object
// FIXME one day support BigInts too!
type Range struct {
	Start Int
	Stop  Int
	Step  Int
	//Length Object
}

// A python Range iterator
type RangeIterator struct {
	Range
	Index Int
}

var RangeType = NewTypeX("range", `range(stop) -> range object
range(start, stop[, step]) -> range object

Return a virtual sequence of numbers from start to stop by step.`,
	RangeNew, nil)

var RangeIteratorType = NewType("range_iterator", `range_iterator object`)

// Type of this object
func (o *Range) Type() *Type {
	return RangeType
}

// Type of this object
func (o *RangeIterator) Type() *Type {
	return RangeIteratorType
}

// RangeNew
func RangeNew(metatype *Type, args Tuple, kwargs StringDict) Object {
	var start Object
	var stop Object
	var step Object = Int(1)
	UnpackTuple(args, kwargs, "range", 1, 3, &start, &stop, &step)
	if len(args) == 1 {
		return &Range{
			Start: Int(0),
			Stop:  Index(start),
			Step:  Int(1),
		}
	}
	return &Range{
		Start: Index(start),
		Stop:  Index(stop),
		Step:  Index(step),
	}
}

// Make a range iterator from a range
func (r *Range) M__iter__() Object {
	return &RangeIterator{
		Range: *r,
		Index: r.Start,
	}
}

// Range iterator
func (it *RangeIterator) M__iter__() Object {
	return it
}

// Range iterator next
func (it *RangeIterator) M__next__() Object {
	r := it.Index
	if r >= it.Stop {
		panic(StopIteration)
	}
	it.Index += it.Step
	return r
}

// Check interface is satisfied
var _ I__iter__ = (*Range)(nil)
var _ I_iterator = (*RangeIterator)(nil)
<|endoftext|>


<commit_before>package checker

import (
	"bytes"
	"errors"
	"io"
	"net/url"
	"strings"

	"golang.org/x/net/html"

	"github.com/asciimoo/privacyscore/penalty"
	"github.com/asciimoo/privacyscore/utils"
)

type HTMLChecker struct{}

func (_ *HTMLChecker) Check(c *CheckJob, p *PageInfo) {
	if !strings.Contains(strings.ToLower(p.ContentType), "html") {
		return
	}
	forbidsReferrer := false
	hasHTTPLink := false
	scriptTagFound := false
	t := html.NewTokenizer(bytes.NewReader(p.ResponseBody))
	for {
		tagToken := t.Next()
		if tagToken == html.ErrorToken {
			if t.Err() == io.EOF {
				break
			} else {
				c.Result.AddError(errors.New("Invalid HTML content"))
				return
			}
		}
		if tagToken != html.StartTagToken && tagToken != html.SelfClosingTagToken {
			continue
		}
		tagName, _ := t.TagName()
		switch string(tagName) {
		case "script":
			if !scriptTagFound {
				c.Result.Penalties.Add(penalty.P_JS)
				scriptTagFound = true
			}
			src, found := getAttr(t, "src")
			if found {
				handleSiteURL(src, c, p, penalty.P_EXTERNAL_RESOURCE)
			}
		case "iframe":
			src, found := getAttr(t, "src")
			if found {
				handleSiteURL(src, c, p, penalty.P_IFRAME)
			}
		case "link":
			attrs := getAttrs(t)
			if rel, found := attrs["rel"]; !found || rel != "stylesheet" {
				break
			}
			if src, found := attrs["href"]; found {
				handleSiteURL(src, c, p, penalty.P_EXTERNAL_RESOURCE)
			}
		case "img":
			src, found := getAttr(t, "src")
			if found {
				handleSiteURL(src, c, p, penalty.P_EXTERNAL_RESOURCE)
			}
		case "meta":
			attrs := getAttrs(t)
			if _, found := attrs["name"]; !found || attrs["name"] != "referrer" {
				break
			}
			if _, found := attrs["content"]; !found {
				break
			}
			switch strings.ToLower(attrs["content"]) {
			case "never", "none", "origin", "no-referrer":
				forbidsReferrer = true
			}
		case "a":
			attrs := getAttrs(t)
			src, found := attrs["href"]
			if !found {
				break
			}
			noreferrer := false
			if rel, found := attrs["rel"]; found && rel == "noreferrer" {
				noreferrer = true
			}
			u, err := url.Parse(src)
			if err != nil {
				break
			}
			if (u.Scheme == "" && p.URL.Scheme != "https") || u.Scheme == "http" {
				hasHTTPLink = true
			}
			if !forbidsReferrer && !noreferrer && utils.IsForeignHost(u.Host, p.Domain) {
				c.Result.Penalties.Add(penalty.P_EXTERNAL_LINK, utils.CropSubdomains(u.Host))
			}
		}
	}
	if hasHTTPLink {
		c.Result.Penalties.Add(penalty.P_HTTP_LINK)
	}
}

func handleSiteURL(URL string, c *CheckJob, p *PageInfo, penalty penalty.PenaltyType) {
	u, err := url.Parse(URL)
	if err != nil {
		return
	}
	if utils.IsForeignHost(u.Host, p.Domain) {
		c.Result.Penalties.Add(penalty, utils.CropSubdomains(u.Host))
	} else {
		c.CheckURL(utils.GetFullURL(u, p.URL))
	}
}

func getAttr(t *html.Tokenizer, name string) (string, bool) {
	for {
		attrName, attrValue, more := t.TagAttr()
		if string(attrName) == name {
			return string(attrValue), true
		}
		if !more {
			break
		}
	}
	return "", false
}

func getAttrs(t *html.Tokenizer) map[string]string {
	attrs := make(map[string]string)
	for {
		attrName, attrValue, more := t.TagAttr()
		attrs[string(attrName)] = string(attrValue)
		if !more {
			break
		}
	}
	return attrs
}
<commit_msg>[fix] browser does not send referrer from https to http<commit_after>package checker

import (
	"bytes"
	"errors"
	"io"
	"net/url"
	"strings"

	"golang.org/x/net/html"

	"github.com/asciimoo/privacyscore/penalty"
	"github.com/asciimoo/privacyscore/utils"
)

type HTMLChecker struct{}

func (_ *HTMLChecker) Check(c *CheckJob, p *PageInfo) {
	if !strings.Contains(strings.ToLower(p.ContentType), "html") {
		return
	}
	forbidsReferrer := false
	hasHTTPLink := false
	scriptTagFound := false
	t := html.NewTokenizer(bytes.NewReader(p.ResponseBody))
	for {
		tagToken := t.Next()
		if tagToken == html.ErrorToken {
			if t.Err() == io.EOF {
				break
			} else {
				c.Result.AddError(errors.New("Invalid HTML content"))
				return
			}
		}
		if tagToken != html.StartTagToken && tagToken != html.SelfClosingTagToken {
			continue
		}
		tagName, _ := t.TagName()
		switch string(tagName) {
		case "script":
			if !scriptTagFound {
				c.Result.Penalties.Add(penalty.P_JS)
				scriptTagFound = true
			}
			src, found := getAttr(t, "src")
			if found {
				handleSiteURL(src, c, p, penalty.P_EXTERNAL_RESOURCE)
			}
		case "iframe":
			src, found := getAttr(t, "src")
			if found {
				handleSiteURL(src, c, p, penalty.P_IFRAME)
			}
		case "link":
			attrs := getAttrs(t)
			if rel, found := attrs["rel"]; !found || rel != "stylesheet" {
				break
			}
			if src, found := attrs["href"]; found {
				handleSiteURL(src, c, p, penalty.P_EXTERNAL_RESOURCE)
			}
		case "img":
			src, found := getAttr(t, "src")
			if found {
				handleSiteURL(src, c, p, penalty.P_EXTERNAL_RESOURCE)
			}
		case "meta":
			attrs := getAttrs(t)
			if _, found := attrs["name"]; !found || attrs["name"] != "referrer" {
				break
			}
			if _, found := attrs["content"]; !found {
				break
			}
			switch strings.ToLower(attrs["content"]) {
			case "never", "none", "origin", "no-referrer":
				forbidsReferrer = true
			}
		case "a":
			attrs := getAttrs(t)
			src, found := attrs["href"]
			if !found {
				break
			}
			noreferrer := false
			if rel, found := attrs["rel"]; found && rel == "noreferrer" {
				noreferrer = true
			}
			u, err := url.Parse(src)
			if err != nil {
				break
			}
			if (u.Scheme == "" && p.URL.Scheme != "https") || u.Scheme == "http" {
				hasHTTPLink = true
			}
			if !forbidsReferrer && !noreferrer && !(p.URL.Scheme == "https" && u.Scheme == "http") && utils.IsForeignHost(u.Host, p.Domain) {
				c.Result.Penalties.Add(penalty.P_EXTERNAL_LINK, utils.CropSubdomains(u.Host))
			}
		}
	}
	if hasHTTPLink {
		c.Result.Penalties.Add(penalty.P_HTTP_LINK)
	}
}

func handleSiteURL(URL string, c *CheckJob, p *PageInfo, penalty penalty.PenaltyType) {
	u, err := url.Parse(URL)
	if err != nil {
		return
	}
	if utils.IsForeignHost(u.Host, p.Domain) {
		c.Result.Penalties.Add(penalty, utils.CropSubdomains(u.Host))
	} else {
		c.CheckURL(utils.GetFullURL(u, p.URL))
	}
}

func getAttr(t *html.Tokenizer, name string) (string, bool) {
	for {
		attrName, attrValue, more := t.TagAttr()
		if string(attrName) == name {
			return string(attrValue), true
		}
		if !more {
			break
		}
	}
	return "", false
}

func getAttrs(t *html.Tokenizer) map[string]string {
	attrs := make(map[string]string)
	for {
		attrName, attrValue, more := t.TagAttr()
		attrs[string(attrName)] = string(attrValue)
		if !more {
			break
		}
	}
	return attrs
}
<|endoftext|>


<commit_before>package configuration

import (
	"fmt"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"

	"encoding/base64"

	"github.com/spf13/viper"
)

const (
	// Constants for viper variable names. Will be used to set
	// default values as well as to get each value

	varPostgresHost                    = "postgres.host"
	varPostgresPort                    = "postgres.port"
	varPostgresUser                    = "postgres.user"
	varPostgresDatabase                = "postgres.database"
	varPostgresPassword                = "postgres.password"
	varPostgresSSLMode                 = "postgres.sslmode"
	varPostgresConnectionTimeout       = "postgres.connection.timeout"
	varPostgresConnectionRetrySleep    = "postgres.connection.retrysleep"
	varPostgresConnectionMaxIdle       = "postgres.connection.maxidle"
	varPostgresConnectionMaxOpen       = "postgres.connection.maxopen"
	varHTTPAddress                     = "http.address"
	varDeveloperModeEnabled            = "developer.mode.enabled"
	varKeycloakRealm                   = "keycloak.realm"
	varKeycloakOpenshiftBroker         = "keycloak.openshift.broker"
	varKeycloakURL                     = "keycloak.url"
	varOpenshiftTenantMasterURL        = "openshift.tenant.masterurl"
	varOpenshiftCheVersion             = "openshift.che.version"
	varOpenshiftJenkinsVersion         = "openshift.jenkins.version"
	varOpenshiftTeamVersion            = "openshift.team.version"
	varOpenshiftTemplateDir            = "openshift.template.dir"
	varOpenshiftServiceToken           = "openshift.service.token"
	varOpenshiftUseCurrentCluster      = "openshift.use.current.cluster"
	varTemplateRecommenderExternalName = "template.recommender.external.name"
	varTemplateRecommenderAPIToken     = "template.recommender.api.token"
	varTemplateDomain                  = "template.domain"
	varWitURL                          = "wit.url"
	varAPIServerInsecureSkipTLSVerify  = "api.server.insecure.skip.tls.verify"
	varLogLevel                        = "log.level"
	varLogJSON                         = "log.json"
)

// Data encapsulates the Viper configuration object which stores the configuration data in-memory.
type Data struct {
	v *viper.Viper
}

// NewData creates a configuration reader object using a configurable configuration file path
func NewData() (*Data, error) {
	c := Data{
		v: viper.New(),
	}
	c.v.SetEnvPrefix("F8")
	c.v.AutomaticEnv()
	c.v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	c.v.SetTypeByDefaultValue(true)
	c.setConfigDefaults()

	return &c, nil
}

// String returns the current configuration as a string
func (c *Data) String() string {
	allSettings := c.v.AllSettings()
	y, err := yaml.Marshal(&allSettings)
	if err != nil {
		log.WithFields(map[string]interface{}{
			"settings": allSettings,
			"err":      err,
		}).Panicln("Failed to marshall config to string")
	}
	return fmt.Sprintf("%s\n", y)
}

// GetData is a wrapper over NewData which reads configuration file path
// from the environment variable.
func GetData() (*Data, error) {
	cd, err := NewData()
	return cd, err
}

func (c *Data) setConfigDefaults() {
	//---------
	// Postgres
	//---------
	c.v.SetTypeByDefaultValue(true)
	c.v.SetDefault(varPostgresHost, "localhost")
	c.v.SetDefault(varPostgresPort, 5432)
	c.v.SetDefault(varPostgresUser, "postgres")
	c.v.SetDefault(varPostgresDatabase, "tenant")
	c.v.SetDefault(varPostgresPassword, "mysecretpassword")
	c.v.SetDefault(varPostgresSSLMode, "disable")
	c.v.SetDefault(varPostgresConnectionTimeout, 5)
	c.v.SetDefault(varPostgresConnectionMaxIdle, -1)
	c.v.SetDefault(varPostgresConnectionMaxOpen, -1)

	// Number of seconds to wait before trying to connect again
	c.v.SetDefault(varPostgresConnectionRetrySleep, time.Duration(time.Second))

	//-----
	// HTTP
	//-----
	c.v.SetDefault(varHTTPAddress, "0.0.0.0:8080")

	//-----
	// Misc
	//-----
	c.v.SetDefault(varKeycloakOpenshiftBroker, defaultKeycloakOpenshiftBroker)
	c.v.SetDefault(varOpenshiftUseCurrentCluster, false)
	c.v.SetDefault(varAPIServerInsecureSkipTLSVerify, false)

	// Enable development related features, e.g. token generation endpoint
	c.v.SetDefault(varDeveloperModeEnabled, false)
	c.v.SetDefault(varLogLevel, defaultLogLevel)

	// HTTP Cache-Control/max-age default
	c.v.SetDefault(varOpenshiftTenantMasterURL, defaultOpenshiftTenantMasterURL)

}

// GetPostgresHost returns the postgres host as set via default, config file, or environment variable
func (c *Data) GetPostgresHost() string {
	return c.v.GetString(varPostgresHost)
}

// GetPostgresPort returns the postgres port as set via default, config file, or environment variable
func (c *Data) GetPostgresPort() int64 {
	return c.v.GetInt64(varPostgresPort)
}

// GetPostgresUser returns the postgres user as set via default, config file, or environment variable
func (c *Data) GetPostgresUser() string {
	return c.v.GetString(varPostgresUser)
}

// GetPostgresDatabase returns the postgres database as set via default, config file, or environment variable
func (c *Data) GetPostgresDatabase() string {
	return c.v.GetString(varPostgresDatabase)
}

// GetPostgresPassword returns the postgres password as set via default, config file, or environment variable
func (c *Data) GetPostgresPassword() string {
	return c.v.GetString(varPostgresPassword)
}

// GetPostgresSSLMode returns the postgres sslmode as set via default, config file, or environment variable
func (c *Data) GetPostgresSSLMode() string {
	return c.v.GetString(varPostgresSSLMode)
}

// GetPostgresConnectionTimeout returns the postgres connection timeout as set via default, config file, or environment variable
func (c *Data) GetPostgresConnectionTimeout() int64 {
	return c.v.GetInt64(varPostgresConnectionTimeout)
}

// GetPostgresConnectionRetrySleep returns the number of seconds (as set via default, config file, or environment variable)
// to wait before trying to connect again
func (c *Data) GetPostgresConnectionRetrySleep() time.Duration {
	return c.v.GetDuration(varPostgresConnectionRetrySleep)
}

// GetPostgresConnectionMaxIdle returns the number of connections that should be keept alive in the database connection pool at
// any given time. -1 represents no restrictions/default behavior
func (c *Data) GetPostgresConnectionMaxIdle() int {
	return c.v.GetInt(varPostgresConnectionMaxIdle)
}

// GetPostgresConnectionMaxOpen returns the max number of open connections that should be open in the database connection pool.
// -1 represents no restrictions/default behavior
func (c *Data) GetPostgresConnectionMaxOpen() int {
	return c.v.GetInt(varPostgresConnectionMaxOpen)
}

// GetPostgresConfigString returns a ready to use string for usage in sql.Open()
func (c *Data) GetPostgresConfigString() string {
	return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s connect_timeout=%d",
		c.GetPostgresHost(),
		c.GetPostgresPort(),
		c.GetPostgresUser(),
		c.GetPostgresPassword(),
		c.GetPostgresDatabase(),
		c.GetPostgresSSLMode(),
		c.GetPostgresConnectionTimeout(),
	)
}

// GetHTTPAddress returns the HTTP address (as set via default, config file, or environment variable)
// that the alm server binds to (e.g. "0.0.0.0:8080")
func (c *Data) GetHTTPAddress() string {
	return c.v.GetString(varHTTPAddress)
}

// IsDeveloperModeEnabled returns if development related features (as set via default, config file, or environment variable),
// e.g. token generation endpoint are enabled
func (c *Data) IsDeveloperModeEnabled() bool {
	return c.v.GetBool(varDeveloperModeEnabled)
}

// GetKeycloakRealm returns the keyclaok realm name
func (c *Data) GetKeycloakRealm() string {
	if c.v.IsSet(varKeycloakRealm) {
		return c.v.GetString(varKeycloakRealm)
	}
	if c.IsDeveloperModeEnabled() {
		return devModeKeycloakRealm
	}
	return defaultKeycloakRealm
}

// GetKeycloakOpenshiftBroker returns the keyclaok broker name for openshift
func (c *Data) GetKeycloakOpenshiftBroker() string {
	return c.v.GetString(varKeycloakOpenshiftBroker)
}

// GetKeycloakURL returns Keycloak URL used by default in Dev mode
func (c *Data) GetKeycloakURL() string {
	if c.v.IsSet(varKeycloakURL) {
		return c.v.GetString(varKeycloakURL)
	}
	if c.IsDeveloperModeEnabled() {
		return devModeKeycloakURL
	}
	return defaultKeycloakURL
}

// GetWitURL returns WIT URL
func (c *Data) GetWitURL() string {
	if c.v.IsSet(varWitURL) {
		return c.v.GetString(varWitURL)
	}
	return defaultWitURL
}

// GetOpenshiftTenantMasterURL returns the URL for the openshift cluster where the tenant services are running
func (c *Data) GetOpenshiftTenantMasterURL() string {
	return c.v.GetString(varOpenshiftTenantMasterURL)
}

// GetOpenshiftTeamVersion returns the team version of YAML files used to provision tenant team namespaces and roles
func (c *Data) GetOpenshiftTeamVersion() string {
	return c.v.GetString(varOpenshiftTeamVersion)
}

// GetOpenshiftCheVersion returns the team version of YAML files used to provision tenant che
func (c *Data) GetOpenshiftCheVersion() string {
	return c.v.GetString(varOpenshiftCheVersion)
}

// GetOpenshiftJenkinsVersion returns the team version of YAML files used to provision tenant jenkins
func (c *Data) GetOpenshiftJenkinsVersion() string {
	return c.v.GetString(varOpenshiftJenkinsVersion)
}

// GetOpenshiftTemplateDir returns the directory containing the local team YAML files
func (c *Data) GetOpenshiftTemplateDir() string {
	return c.v.GetString(varOpenshiftTemplateDir)
}

// GetOpenshiftServiceToken returns the token be used by matser user for tenant init
func (c *Data) GetOpenshiftServiceToken() string {
	return c.v.GetString(varOpenshiftServiceToken)
}

// UseOpenshiftCurrentCluster returns if we should use the current cluster to provision tenant service
func (c *Data) UseOpenshiftCurrentCluster() bool {
	return c.v.GetBool(varOpenshiftUseCurrentCluster)
}

// APIServerInsecureSkipTLSVerify returns if the server's certificate should be checked for validity. This will make your HTTPS connections insecure.
func (c *Data) APIServerInsecureSkipTLSVerify() bool {
	return c.v.GetBool(varAPIServerInsecureSkipTLSVerify)
}

// GetLogLevel returns the loggging level (as set via config file or environment variable)
func (c *Data) GetLogLevel() string {
	return c.v.GetString(varLogLevel)
}

// IsLogJSON returns if we should log json format (as set via config file or environment variable)
func (c *Data) IsLogJSON() bool {
	if c.v.IsSet(varLogJSON) {
		return c.v.GetBool(varLogJSON)
	}
	if c.IsDeveloperModeEnabled() {
		return false
	}
	return true
}

// GetTemplateValues return a Map of additional variables used to process the templates
func (c *Data) GetTemplateValues() (map[string]string, error) {
	if !c.v.IsSet(varTemplateRecommenderExternalName) {
		return nil, fmt.Errorf("Missing required configuration %v", varTemplateRecommenderExternalName)
	}
	if !c.v.IsSet(varTemplateRecommenderAPIToken) {
		return nil, fmt.Errorf("Missing required configuration %v", varTemplateRecommenderAPIToken)
	}
	if !c.v.IsSet(varTemplateDomain) {
		return nil, fmt.Errorf("Missing required configuration %v", varTemplateDomain)
	}

	return map[string]string{
		"RECOMMENDER_EXTERNAL_NAME": c.v.GetString(varTemplateRecommenderExternalName),
		"RECOMMENDER_API_TOKEN":     base64.StdEncoding.EncodeToString([]byte(c.v.GetString(varTemplateRecommenderAPIToken))),
		"DOMAIN":                    c.v.GetString(varTemplateDomain),
	}, nil
}

const (
	// Auth-related defaults

	defaultKeycloakURL             = "https://sso.prod-preview.openshift.io"
	defaultKeycloakRealm           = "fabric8"
	defaultKeycloakOpenshiftBroker = "openshift-v3"

	// Keycloak vars to be used in dev mode. Can be overridden by setting up keycloak.url & keycloak.realm
	devModeKeycloakURL   = "https://sso.prod-preview.openshift.io"
	devModeKeycloakRealm = "fabric8-test"

	defaultOpenshiftTenantMasterURL = "https://api.free-int.openshift.com"

	defaultLogLevel = "info"

	defaultWitURL = "https://api.openshift.io/"
)
<commit_msg>include /api/ in the default URL for WIT (#299)<commit_after>package configuration

import (
	"fmt"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"

	"encoding/base64"

	"github.com/spf13/viper"
)

const (
	// Constants for viper variable names. Will be used to set
	// default values as well as to get each value

	varPostgresHost                    = "postgres.host"
	varPostgresPort                    = "postgres.port"
	varPostgresUser                    = "postgres.user"
	varPostgresDatabase                = "postgres.database"
	varPostgresPassword                = "postgres.password"
	varPostgresSSLMode                 = "postgres.sslmode"
	varPostgresConnectionTimeout       = "postgres.connection.timeout"
	varPostgresConnectionRetrySleep    = "postgres.connection.retrysleep"
	varPostgresConnectionMaxIdle       = "postgres.connection.maxidle"
	varPostgresConnectionMaxOpen       = "postgres.connection.maxopen"
	varHTTPAddress                     = "http.address"
	varDeveloperModeEnabled            = "developer.mode.enabled"
	varKeycloakRealm                   = "keycloak.realm"
	varKeycloakOpenshiftBroker         = "keycloak.openshift.broker"
	varKeycloakURL                     = "keycloak.url"
	varOpenshiftTenantMasterURL        = "openshift.tenant.masterurl"
	varOpenshiftCheVersion             = "openshift.che.version"
	varOpenshiftJenkinsVersion         = "openshift.jenkins.version"
	varOpenshiftTeamVersion            = "openshift.team.version"
	varOpenshiftTemplateDir            = "openshift.template.dir"
	varOpenshiftServiceToken           = "openshift.service.token"
	varOpenshiftUseCurrentCluster      = "openshift.use.current.cluster"
	varTemplateRecommenderExternalName = "template.recommender.external.name"
	varTemplateRecommenderAPIToken     = "template.recommender.api.token"
	varTemplateDomain                  = "template.domain"
	varWitURL                          = "wit.url"
	varAPIServerInsecureSkipTLSVerify  = "api.server.insecure.skip.tls.verify"
	varLogLevel                        = "log.level"
	varLogJSON                         = "log.json"
)

// Data encapsulates the Viper configuration object which stores the configuration data in-memory.
type Data struct {
	v *viper.Viper
}

// NewData creates a configuration reader object using a configurable configuration file path
func NewData() (*Data, error) {
	c := Data{
		v: viper.New(),
	}
	c.v.SetEnvPrefix("F8")
	c.v.AutomaticEnv()
	c.v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	c.v.SetTypeByDefaultValue(true)
	c.setConfigDefaults()

	return &c, nil
}

// String returns the current configuration as a string
func (c *Data) String() string {
	allSettings := c.v.AllSettings()
	y, err := yaml.Marshal(&allSettings)
	if err != nil {
		log.WithFields(map[string]interface{}{
			"settings": allSettings,
			"err":      err,
		}).Panicln("Failed to marshall config to string")
	}
	return fmt.Sprintf("%s\n", y)
}

// GetData is a wrapper over NewData which reads configuration file path
// from the environment variable.
func GetData() (*Data, error) {
	cd, err := NewData()
	return cd, err
}

func (c *Data) setConfigDefaults() {
	//---------
	// Postgres
	//---------
	c.v.SetTypeByDefaultValue(true)
	c.v.SetDefault(varPostgresHost, "localhost")
	c.v.SetDefault(varPostgresPort, 5432)
	c.v.SetDefault(varPostgresUser, "postgres")
	c.v.SetDefault(varPostgresDatabase, "tenant")
	c.v.SetDefault(varPostgresPassword, "mysecretpassword")
	c.v.SetDefault(varPostgresSSLMode, "disable")
	c.v.SetDefault(varPostgresConnectionTimeout, 5)
	c.v.SetDefault(varPostgresConnectionMaxIdle, -1)
	c.v.SetDefault(varPostgresConnectionMaxOpen, -1)

	// Number of seconds to wait before trying to connect again
	c.v.SetDefault(varPostgresConnectionRetrySleep, time.Duration(time.Second))

	//-----
	// HTTP
	//-----
	c.v.SetDefault(varHTTPAddress, "0.0.0.0:8080")

	//-----
	// Misc
	//-----
	c.v.SetDefault(varKeycloakOpenshiftBroker, defaultKeycloakOpenshiftBroker)
	c.v.SetDefault(varOpenshiftUseCurrentCluster, false)
	c.v.SetDefault(varAPIServerInsecureSkipTLSVerify, false)

	// Enable development related features, e.g. token generation endpoint
	c.v.SetDefault(varDeveloperModeEnabled, false)
	c.v.SetDefault(varLogLevel, defaultLogLevel)

	// HTTP Cache-Control/max-age default
	c.v.SetDefault(varOpenshiftTenantMasterURL, defaultOpenshiftTenantMasterURL)

}

// GetPostgresHost returns the postgres host as set via default, config file, or environment variable
func (c *Data) GetPostgresHost() string {
	return c.v.GetString(varPostgresHost)
}

// GetPostgresPort returns the postgres port as set via default, config file, or environment variable
func (c *Data) GetPostgresPort() int64 {
	return c.v.GetInt64(varPostgresPort)
}

// GetPostgresUser returns the postgres user as set via default, config file, or environment variable
func (c *Data) GetPostgresUser() string {
	return c.v.GetString(varPostgresUser)
}

// GetPostgresDatabase returns the postgres database as set via default, config file, or environment variable
func (c *Data) GetPostgresDatabase() string {
	return c.v.GetString(varPostgresDatabase)
}

// GetPostgresPassword returns the postgres password as set via default, config file, or environment variable
func (c *Data) GetPostgresPassword() string {
	return c.v.GetString(varPostgresPassword)
}

// GetPostgresSSLMode returns the postgres sslmode as set via default, config file, or environment variable
func (c *Data) GetPostgresSSLMode() string {
	return c.v.GetString(varPostgresSSLMode)
}

// GetPostgresConnectionTimeout returns the postgres connection timeout as set via default, config file, or environment variable
func (c *Data) GetPostgresConnectionTimeout() int64 {
	return c.v.GetInt64(varPostgresConnectionTimeout)
}

// GetPostgresConnectionRetrySleep returns the number of seconds (as set via default, config file, or environment variable)
// to wait before trying to connect again
func (c *Data) GetPostgresConnectionRetrySleep() time.Duration {
	return c.v.GetDuration(varPostgresConnectionRetrySleep)
}

// GetPostgresConnectionMaxIdle returns the number of connections that should be keept alive in the database connection pool at
// any given time. -1 represents no restrictions/default behavior
func (c *Data) GetPostgresConnectionMaxIdle() int {
	return c.v.GetInt(varPostgresConnectionMaxIdle)
}

// GetPostgresConnectionMaxOpen returns the max number of open connections that should be open in the database connection pool.
// -1 represents no restrictions/default behavior
func (c *Data) GetPostgresConnectionMaxOpen() int {
	return c.v.GetInt(varPostgresConnectionMaxOpen)
}

// GetPostgresConfigString returns a ready to use string for usage in sql.Open()
func (c *Data) GetPostgresConfigString() string {
	return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s connect_timeout=%d",
		c.GetPostgresHost(),
		c.GetPostgresPort(),
		c.GetPostgresUser(),
		c.GetPostgresPassword(),
		c.GetPostgresDatabase(),
		c.GetPostgresSSLMode(),
		c.GetPostgresConnectionTimeout(),
	)
}

// GetHTTPAddress returns the HTTP address (as set via default, config file, or environment variable)
// that the alm server binds to (e.g. "0.0.0.0:8080")
func (c *Data) GetHTTPAddress() string {
	return c.v.GetString(varHTTPAddress)
}

// IsDeveloperModeEnabled returns if development related features (as set via default, config file, or environment variable),
// e.g. token generation endpoint are enabled
func (c *Data) IsDeveloperModeEnabled() bool {
	return c.v.GetBool(varDeveloperModeEnabled)
}

// GetKeycloakRealm returns the keyclaok realm name
func (c *Data) GetKeycloakRealm() string {
	if c.v.IsSet(varKeycloakRealm) {
		return c.v.GetString(varKeycloakRealm)
	}
	if c.IsDeveloperModeEnabled() {
		return devModeKeycloakRealm
	}
	return defaultKeycloakRealm
}

// GetKeycloakOpenshiftBroker returns the keyclaok broker name for openshift
func (c *Data) GetKeycloakOpenshiftBroker() string {
	return c.v.GetString(varKeycloakOpenshiftBroker)
}

// GetKeycloakURL returns Keycloak URL used by default in Dev mode
func (c *Data) GetKeycloakURL() string {
	if c.v.IsSet(varKeycloakURL) {
		return c.v.GetString(varKeycloakURL)
	}
	if c.IsDeveloperModeEnabled() {
		return devModeKeycloakURL
	}
	return defaultKeycloakURL
}

// GetWitURL returns WIT URL
func (c *Data) GetWitURL() string {
	if c.v.IsSet(varWitURL) {
		return c.v.GetString(varWitURL)
	}
	return defaultWitURL
}

// GetOpenshiftTenantMasterURL returns the URL for the openshift cluster where the tenant services are running
func (c *Data) GetOpenshiftTenantMasterURL() string {
	return c.v.GetString(varOpenshiftTenantMasterURL)
}

// GetOpenshiftTeamVersion returns the team version of YAML files used to provision tenant team namespaces and roles
func (c *Data) GetOpenshiftTeamVersion() string {
	return c.v.GetString(varOpenshiftTeamVersion)
}

// GetOpenshiftCheVersion returns the team version of YAML files used to provision tenant che
func (c *Data) GetOpenshiftCheVersion() string {
	return c.v.GetString(varOpenshiftCheVersion)
}

// GetOpenshiftJenkinsVersion returns the team version of YAML files used to provision tenant jenkins
func (c *Data) GetOpenshiftJenkinsVersion() string {
	return c.v.GetString(varOpenshiftJenkinsVersion)
}

// GetOpenshiftTemplateDir returns the directory containing the local team YAML files
func (c *Data) GetOpenshiftTemplateDir() string {
	return c.v.GetString(varOpenshiftTemplateDir)
}

// GetOpenshiftServiceToken returns the token be used by matser user for tenant init
func (c *Data) GetOpenshiftServiceToken() string {
	return c.v.GetString(varOpenshiftServiceToken)
}

// UseOpenshiftCurrentCluster returns if we should use the current cluster to provision tenant service
func (c *Data) UseOpenshiftCurrentCluster() bool {
	return c.v.GetBool(varOpenshiftUseCurrentCluster)
}

// APIServerInsecureSkipTLSVerify returns if the server's certificate should be checked for validity. This will make your HTTPS connections insecure.
func (c *Data) APIServerInsecureSkipTLSVerify() bool {
	return c.v.GetBool(varAPIServerInsecureSkipTLSVerify)
}

// GetLogLevel returns the loggging level (as set via config file or environment variable)
func (c *Data) GetLogLevel() string {
	return c.v.GetString(varLogLevel)
}

// IsLogJSON returns if we should log json format (as set via config file or environment variable)
func (c *Data) IsLogJSON() bool {
	if c.v.IsSet(varLogJSON) {
		return c.v.GetBool(varLogJSON)
	}
	if c.IsDeveloperModeEnabled() {
		return false
	}
	return true
}

// GetTemplateValues return a Map of additional variables used to process the templates
func (c *Data) GetTemplateValues() (map[string]string, error) {
	if !c.v.IsSet(varTemplateRecommenderExternalName) {
		return nil, fmt.Errorf("Missing required configuration %v", varTemplateRecommenderExternalName)
	}
	if !c.v.IsSet(varTemplateRecommenderAPIToken) {
		return nil, fmt.Errorf("Missing required configuration %v", varTemplateRecommenderAPIToken)
	}
	if !c.v.IsSet(varTemplateDomain) {
		return nil, fmt.Errorf("Missing required configuration %v", varTemplateDomain)
	}

	return map[string]string{
		"RECOMMENDER_EXTERNAL_NAME": c.v.GetString(varTemplateRecommenderExternalName),
		"RECOMMENDER_API_TOKEN":     base64.StdEncoding.EncodeToString([]byte(c.v.GetString(varTemplateRecommenderAPIToken))),
		"DOMAIN":                    c.v.GetString(varTemplateDomain),
	}, nil
}

const (
	// Auth-related defaults

	defaultKeycloakURL             = "https://sso.prod-preview.openshift.io"
	defaultKeycloakRealm           = "fabric8"
	defaultKeycloakOpenshiftBroker = "openshift-v3"

	// Keycloak vars to be used in dev mode. Can be overridden by setting up keycloak.url & keycloak.realm
	devModeKeycloakURL   = "https://sso.prod-preview.openshift.io"
	devModeKeycloakRealm = "fabric8-test"

	defaultOpenshiftTenantMasterURL = "https://api.free-int.openshift.com"

	defaultLogLevel = "info"

	defaultWitURL = "https://api.openshift.io/api/"
)
<|endoftext|>


<commit_before>package main

type Productor struct {
	items   [][]string
	indexes []int
	ch      chan []int
}

func NewProductor(items [][]string, ch chan []int) *Productor {
	return &Productor{
		items:   items,
		indexes: make([]int, len(items)),
		ch:      ch,
	}
}

func (p *Productor) FindProduct(index_i int) {
	if index_i == len(p.items) {
		indexes := make([]int, len(p.indexes))
		copy(indexes, p.indexes)
		p.ch <- indexes
		return
	}

	for i := 0; i < len(p.items[index_i]); i++ {
		p.indexes[index_i] = i
		p.FindProduct(index_i + 1)
	}
}

func Product(items [][]string) chan []int {
	ch := make(chan []int, 16)
	go func() {
		p := NewProductor(items, ch)
		p.FindProduct(0)
		close(p.ch)
	}()
	return ch
}
<commit_msg>Remove an argument of Productor.FindProduct<commit_after>package main

type Productor struct {
	items   [][]string
	indexes []int
	ch      chan []int
}

func NewProductor(items [][]string, ch chan []int) *Productor {
	return &Productor{
		items:   items,
		indexes: make([]int, len(items)),
		ch:      ch,
	}
}

func (p *Productor) findProduct(index_i int) {
	if index_i == len(p.items) {
		indexes := make([]int, len(p.indexes))
		copy(indexes, p.indexes)
		p.ch <- indexes
		return
	}

	for i := 0; i < len(p.items[index_i]); i++ {
		p.indexes[index_i] = i
		p.findProduct(index_i + 1)
	}
}

func (p *Productor) FindProduct() {
	p.findProduct(0)
}

func Product(items [][]string) chan []int {
	ch := make(chan []int, 16)
	go func() {
		p := NewProductor(items, ch)
		p.FindProduct()
		close(p.ch)
	}()
	return ch
}
<|endoftext|>


<commit_before>// Copyright 2015 gandalf authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/codegangsta/negroni"
	"gopkg.in/check.v1"
)

func (s *S) TestLoggerMiddleware(c *check.C) {
	recorder := httptest.NewRecorder()
	request, err := http.NewRequest("PUT", "/my/path", nil)
	c.Assert(err, check.IsNil)
	var out bytes.Buffer
	middle := loggerMiddleware{
		logger: log.New(&out, "", 0),
	}
	middle.ServeHTTP(negroni.NewResponseWriter(recorder), request, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	}))
	timePart := time.Now().Format(time.RFC3339Nano)[:19]
	c.Assert(out.String(), check.Matches, fmt.Sprintf(`(?m)%s\..+? PUT /my/path 200 in [\d.]+ms$`, timePart))
}
<commit_msg>api: add tests for the responseHeaderMiddleware<commit_after>// Copyright 2015 gandalf authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/codegangsta/negroni"
	"gopkg.in/check.v1"
)

func (s *S) TestLoggerMiddleware(c *check.C) {
	recorder := httptest.NewRecorder()
	request, err := http.NewRequest("PUT", "/my/path", nil)
	c.Assert(err, check.IsNil)
	var out bytes.Buffer
	middle := loggerMiddleware{
		logger: log.New(&out, "", 0),
	}
	middle.ServeHTTP(negroni.NewResponseWriter(recorder), request, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	}))
	timePart := time.Now().Format(time.RFC3339Nano)[:19]
	c.Assert(out.String(), check.Matches, fmt.Sprintf(`(?m)%s\..+? PUT /my/path 200 in [\d.]+ms$`, timePart))
}

func (s *S) TestResponseHeaderMiddleware(c *check.C) {
	recorder := httptest.NewRecorder()
	request, err := http.NewRequest("PUT", "/my/path", nil)
	c.Assert(err, check.IsNil)
	middle := responseHeaderMiddleware{name: "Server", value: "super-server/0.1"}
	middle.ServeHTTP(negroni.NewResponseWriter(recorder), request, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	}))
	c.Assert(recorder.Header().Get("Server"), check.Equals, "super-server/0.1")
}

func (s *S) TestResponseHeaderMiddlewareDuplicate(c *check.C) {
	recorder := httptest.NewRecorder()
	request, err := http.NewRequest("PUT", "/my/path", nil)
	c.Assert(err, check.IsNil)
	middle := responseHeaderMiddleware{name: "Server", value: "super-server/0.1"}
	middle.ServeHTTP(negroni.NewResponseWriter(recorder), request, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Server", "mini-server/0.1")
		w.Write([]byte("hello"))
	}))
	c.Assert(recorder.Header().Get("Server"), check.Equals, "mini-server/0.1")
}
<|endoftext|>


<commit_before>package zclsyntax

import (
	"bytes"
	"fmt"

	"github.com/zclconf/go-zcl/zcl"
)

type parser struct {
	*peeker

	// set to true if any recovery is attempted. The parser can use this
	// to attempt to reduce error noise by suppressing "bad token" errors
	// in recovery mode, assuming that the recovery heuristics have failed
	// in this case and left the peeker in a wrong place.
	recovery bool
}

func (p *parser) ParseBody(end TokenType) (*Body, zcl.Diagnostics) {
	attrs := Attributes{}
	blocks := Blocks{}
	var diags zcl.Diagnostics

	startRange := p.PrevRange()
	var endRange zcl.Range

Token:
	for {
		next := p.Peek()
		if next.Type == end {
			endRange = p.NextRange()
			p.Read()
			break Token
		}

		switch next.Type {
		case TokenNewline:
			p.Read()
			continue
		case TokenIdent:
			item, itemDiags := p.ParseBodyItem()
			diags = append(diags, itemDiags...)
			switch titem := item.(type) {
			case *Block:
				blocks = append(blocks, titem)
			case *Attribute:
				if existing, exists := attrs[titem.Name]; exists {
					diags = append(diags, &zcl.Diagnostic{
						Severity: zcl.DiagError,
						Summary:  "Attribute redefined",
						Detail: fmt.Sprintf(
							"The attribute %q was already defined at %s. Each attribute may be defined only once.",
							titem.Name, existing.NameRange.String(),
						),
						Subject: &titem.NameRange,
					})
				} else {
					attrs[titem.Name] = titem
				}
			default:
				// This should never happen for valid input, but may if a
				// syntax error was detected in ParseBodyItem that prevented
				// it from even producing a partially-broken item. In that
				// case, it would've left at least one error in the diagnostics
				// slice we already dealt with above.
				//
				// We'll assume ParseBodyItem attempted recovery to leave
				// us in a reasonable position to try parsing the next item.
				continue
			}
		default:
			bad := p.Read()
			if bad.Type == TokenOQuote {
				diags = append(diags, &zcl.Diagnostic{
					Severity: zcl.DiagError,
					Summary:  "Invalid attribute name",
					Detail:   "Attribute names must not be quoted.",
					Subject:  &bad.Range,
				})
			} else {
				diags = append(diags, &zcl.Diagnostic{
					Severity: zcl.DiagError,
					Summary:  "Attribute or block definition required",
					Detail:   "An attribute or block definition is required here.",
					Subject:  &bad.Range,
				})
			}
			endRange = p.PrevRange() // arbitrary, but somewhere inside the body means better diagnostics

			p.recover(end) // attempt to recover to the token after the end of this body
			break Token
		}
	}

	return &Body{
		Attributes: attrs,
		Blocks:     blocks,

		SrcRange: zcl.RangeBetween(startRange, endRange),
		EndRange: zcl.Range{
			Filename: endRange.Filename,
			Start:    endRange.End,
			End:      endRange.End,
		},
	}, diags
}

func (p *parser) ParseBodyItem() (Node, zcl.Diagnostics) {
	ident := p.Read()
	if ident.Type != TokenIdent {
		p.recoverAfterBodyItem()
		return nil, zcl.Diagnostics{
			{
				Severity: zcl.DiagError,
				Summary:  "Attribute or block definition required",
				Detail:   "An attribute or block definition is required here.",
				Subject:  &ident.Range,
			},
		}
	}

	next := p.Peek()

	switch next.Type {
	case TokenEqual:
		return p.finishParsingBodyAttribute(ident)
	case TokenOQuote, TokenOBrace:
		return p.finishParsingBodyBlock(ident)
	default:
		p.recoverAfterBodyItem()
		return nil, zcl.Diagnostics{
			{
				Severity: zcl.DiagError,
				Summary:  "Attribute or block definition required",
				Detail:   "An attribute or block definition is required here. To define an attribute, use the equals sign \"=\" to introduce the attribute value.",
				Subject:  &ident.Range,
			},
		}
	}

	return nil, nil
}

func (p *parser) finishParsingBodyAttribute(ident Token) (Node, zcl.Diagnostics) {
	panic("attribute parsing not yet implemented")
}

func (p *parser) finishParsingBodyBlock(ident Token) (Node, zcl.Diagnostics) {
	var blockType = string(ident.Bytes)
	var diags zcl.Diagnostics
	var labels []string
	var labelRanges []zcl.Range

	var oBrace Token

Token:
	for {
		tok := p.Peek()

		switch tok.Type {

		case TokenOBrace:
			oBrace = p.Read()
			break Token

		case TokenOQuote:
			label, labelRange, labelDiags := p.parseQuotedStringLiteral()
			diags = append(diags, labelDiags...)
			labels = append(labels, label)
			labelRanges = append(labelRanges, labelRange)

		default:
			switch tok.Type {
			case TokenEqual:
				diags = append(diags, &zcl.Diagnostic{
					Severity: zcl.DiagError,
					Summary:  "Invalid block definition",
					Detail:   "The equals sign \"=\" indicates an attribute definition, and must not be used when defining a block.",
					Subject:  &tok.Range,
					Context:  zcl.RangeBetween(ident.Range, tok.Range).Ptr(),
				})
			case TokenNewline:
				diags = append(diags, &zcl.Diagnostic{
					Severity: zcl.DiagError,
					Summary:  "Invalid block definition",
					Detail:   "A block definition must have block content delimited by \"{\" and \"}\", starting on the same line as the block header.",
					Subject:  &tok.Range,
					Context:  zcl.RangeBetween(ident.Range, tok.Range).Ptr(),
				})
			default:
				if !p.recovery {
					diags = append(diags, &zcl.Diagnostic{
						Severity: zcl.DiagError,
						Summary:  "Invalid block definition",
						Detail:   "Either a quoted string block label or an opening brace (\"{\") is expected here.",
						Subject:  &tok.Range,
						Context:  zcl.RangeBetween(ident.Range, tok.Range).Ptr(),
					})
				}
			}

			p.recoverAfterBodyItem()

			return &Block{
				Type:   blockType,
				Labels: labels,
				Body:   nil,

				TypeRange:       ident.Range,
				LabelRanges:     labelRanges,
				OpenBraceRange:  ident.Range, // placeholder
				CloseBraceRange: ident.Range, // placeholder
			}, diags
		}
	}

	// Once we fall out here, the peeker is pointed just after our opening
	// brace, so we can begin our nested body parsing.
	body, bodyDiags := p.ParseBody(TokenCBrace)
	diags = append(diags, bodyDiags...)
	cBraceRange := p.PrevRange()

	return &Block{
		Type:   blockType,
		Labels: labels,
		Body:   body,

		TypeRange:       ident.Range,
		LabelRanges:     labelRanges,
		OpenBraceRange:  oBrace.Range,
		CloseBraceRange: cBraceRange,
	}, diags
}

// parseQuotedStringLiteral is a helper for parsing quoted strings that
// aren't allowed to contain any interpolations, such as block labels.
func (p *parser) parseQuotedStringLiteral() (string, zcl.Range, zcl.Diagnostics) {
	oQuote := p.Read()
	if oQuote.Type != TokenOQuote {
		return "", oQuote.Range, zcl.Diagnostics{
			{
				Severity: zcl.DiagError,
				Summary:  "Invalid string literal",
				Detail:   "A quoted string is required here.",
				Subject:  &oQuote.Range,
			},
		}
	}

	var diags zcl.Diagnostics
	ret := &bytes.Buffer{}
	var cQuote Token

Token:
	for {
		tok := p.Read()
		switch tok.Type {

		case TokenCQuote:
			cQuote = tok
			break Token

		case TokenStringLit:
			// TODO: Remove any escape sequences from the string, once we
			// have a function with which to do that.
			ret.Write(tok.Bytes)

		case TokenTemplateControl, TokenTemplateInterp:
			which := "$"
			if tok.Type == TokenTemplateControl {
				which = "!"
			}

			diags = append(diags, &zcl.Diagnostic{
				Severity: zcl.DiagError,
				Summary:  "Invalid string literal",
				Detail: fmt.Sprintf(
					"Template sequences are not allowed in this string. To include a literal %q, double it (as \"%s%s\") to escape it.",
					which, which, which,
				),
				Subject: &tok.Range,
				Context: zcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
			})
			p.recover(TokenTemplateSeqEnd)

		case TokenEOF:
			diags = append(diags, &zcl.Diagnostic{
				Severity: zcl.DiagError,
				Summary:  "Unterminated string literal",
				Detail:   "Unable to find the closing quote mark before the end of the file.",
				Subject:  &tok.Range,
				Context:  zcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
			})
			break Token

		default:
			// Should never happen, as long as the scanner is behaving itself
			diags = append(diags, &zcl.Diagnostic{
				Severity: zcl.DiagError,
				Summary:  "Invalid string literal",
				Detail:   "This item is not valid in a string literal.",
				Subject:  &tok.Range,
				Context:  zcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
			})
			p.recover(TokenOQuote)
			break Token

		}

	}

	return ret.String(), zcl.RangeBetween(oQuote.Range, cQuote.Range), diags
}

// recover seeks forward in the token stream until it finds TokenType "end",
// then returns with the peeker pointed at the following token.
//
// If the given token type is a bracketer, this function will additionally
// count nested instances of the brackets to try to leave the peeker at
// the end of the _current_ instance of that bracketer, skipping over any
// nested instances. This is a best-effort operation and may have
// unpredictable results on input with bad bracketer nesting.
func (p *parser) recover(end TokenType) {
	start := p.oppositeBracket(end)
	p.recovery = true

	nest := 0
	for {
		tok := p.Read()
		ty := tok.Type
		if end == TokenTemplateSeqEnd && ty == TokenTemplateControl {
			// normalize so that our matching behavior can work, since
			// TokenTemplateControl/TokenTemplateInterp are asymmetrical
			// with TokenTemplateSeqEnd and thus we need to count both
			// openers if that's the closer we're looking for.
			ty = TokenTemplateInterp
		}

		switch ty {
		case start:
			nest++
		case end:
			if nest < 1 {
				return
			}

			nest--
		}
	}
}

// recoverOver seeks forward in the token stream until it finds a block
// starting with TokenType "start", then finds the corresponding end token,
// leaving the peeker pointed at the token after that end token.
//
// The given token type _must_ be a bracketer. For example, if the given
// start token is TokenOBrace then the parser will be left at the _end_ of
// the next brace-delimited block encountered, or at EOF if no such block
// is found or it is unclosed.
func (p *parser) recoverOver(start TokenType) {
	end := p.oppositeBracket(start)

	// find the opening bracket first
Token:
	for {
		tok := p.Read()
		switch tok.Type {
		case start:
			break Token
		}
	}

	// Now use our existing recover function to locate the _end_ of the
	// container we've found.
	p.recover(end)
}

func (p *parser) recoverAfterBodyItem() {
	p.recovery = true
	var open []TokenType

Token:
	for {
		tok := p.Read()

		switch tok.Type {

		case TokenNewline:
			if len(open) == 0 {
				break Token
			}

		case TokenEOF:
			break Token

		case TokenOBrace, TokenOBrack, TokenOParen, TokenOQuote, TokenOHeredoc, TokenTemplateInterp, TokenTemplateControl:
			open = append(open, tok.Type)

		case TokenCBrace, TokenCBrack, TokenCParen, TokenCQuote, TokenCHeredoc:
			opener := p.oppositeBracket(tok.Type)
			for len(open) > 0 && open[len(open)-1] != opener {
				open = open[:len(open)-1]
			}
			if len(open) > 0 {
				open = open[:len(open)-1]
			}

		case TokenTemplateSeqEnd:
			for len(open) > 0 && open[len(open)-1] != TokenTemplateInterp && open[len(open)-1] != TokenTemplateControl {
				open = open[:len(open)-1]
			}
			if len(open) > 0 {
				open = open[:len(open)-1]
			}

		}
	}
}

// oppositeBracket finds the bracket that opposes the given bracketer, or
// NilToken if the given token isn't a bracketer.
//
// "Bracketer", for the sake of this function, is one end of a matching
// open/close set of tokens that establish a bracketing context.
func (p *parser) oppositeBracket(ty TokenType) TokenType {
	switch ty {

	case TokenOBrace:
		return TokenCBrace
	case TokenOBrack:
		return TokenCBrack
	case TokenOParen:
		return TokenCParen
	case TokenOQuote:
		return TokenCQuote
	case TokenOHeredoc:
		return TokenCHeredoc

	case TokenCBrace:
		return TokenOBrace
	case TokenCBrack:
		return TokenOBrack
	case TokenCParen:
		return TokenOParen
	case TokenCQuote:
		return TokenOQuote
	case TokenCHeredoc:
		return TokenOHeredoc

	case TokenTemplateControl:
		return TokenTemplateSeqEnd
	case TokenTemplateInterp:
		return TokenTemplateSeqEnd
	case TokenTemplateSeqEnd:
		// This is ambigous, but we return Interp here because that's
		// what's assumed by the "recover" method.
		return TokenTemplateInterp

	default:
		return TokenNil
	}
}
<commit_msg>zclsyntax: fix parser infinite loops on certain EOF cases<commit_after>package zclsyntax

import (
	"bytes"
	"fmt"

	"github.com/zclconf/go-zcl/zcl"
)

type parser struct {
	*peeker

	// set to true if any recovery is attempted. The parser can use this
	// to attempt to reduce error noise by suppressing "bad token" errors
	// in recovery mode, assuming that the recovery heuristics have failed
	// in this case and left the peeker in a wrong place.
	recovery bool
}

func (p *parser) ParseBody(end TokenType) (*Body, zcl.Diagnostics) {
	attrs := Attributes{}
	blocks := Blocks{}
	var diags zcl.Diagnostics

	startRange := p.PrevRange()
	var endRange zcl.Range

Token:
	for {
		next := p.Peek()
		if next.Type == end {
			endRange = p.NextRange()
			p.Read()
			break Token
		}

		switch next.Type {
		case TokenNewline:
			p.Read()
			continue
		case TokenIdent:
			item, itemDiags := p.ParseBodyItem()
			diags = append(diags, itemDiags...)
			switch titem := item.(type) {
			case *Block:
				blocks = append(blocks, titem)
			case *Attribute:
				if existing, exists := attrs[titem.Name]; exists {
					diags = append(diags, &zcl.Diagnostic{
						Severity: zcl.DiagError,
						Summary:  "Attribute redefined",
						Detail: fmt.Sprintf(
							"The attribute %q was already defined at %s. Each attribute may be defined only once.",
							titem.Name, existing.NameRange.String(),
						),
						Subject: &titem.NameRange,
					})
				} else {
					attrs[titem.Name] = titem
				}
			default:
				// This should never happen for valid input, but may if a
				// syntax error was detected in ParseBodyItem that prevented
				// it from even producing a partially-broken item. In that
				// case, it would've left at least one error in the diagnostics
				// slice we already dealt with above.
				//
				// We'll assume ParseBodyItem attempted recovery to leave
				// us in a reasonable position to try parsing the next item.
				continue
			}
		default:
			bad := p.Read()
			if !p.recovery {
				if bad.Type == TokenOQuote {
					diags = append(diags, &zcl.Diagnostic{
						Severity: zcl.DiagError,
						Summary:  "Invalid attribute name",
						Detail:   "Attribute names must not be quoted.",
						Subject:  &bad.Range,
					})
				} else {
					diags = append(diags, &zcl.Diagnostic{
						Severity: zcl.DiagError,
						Summary:  "Attribute or block definition required",
						Detail:   "An attribute or block definition is required here.",
						Subject:  &bad.Range,
					})
				}
			}
			endRange = p.PrevRange() // arbitrary, but somewhere inside the body means better diagnostics

			p.recover(end) // attempt to recover to the token after the end of this body
			break Token
		}
	}

	return &Body{
		Attributes: attrs,
		Blocks:     blocks,

		SrcRange: zcl.RangeBetween(startRange, endRange),
		EndRange: zcl.Range{
			Filename: endRange.Filename,
			Start:    endRange.End,
			End:      endRange.End,
		},
	}, diags
}

func (p *parser) ParseBodyItem() (Node, zcl.Diagnostics) {
	ident := p.Read()
	if ident.Type != TokenIdent {
		p.recoverAfterBodyItem()
		return nil, zcl.Diagnostics{
			{
				Severity: zcl.DiagError,
				Summary:  "Attribute or block definition required",
				Detail:   "An attribute or block definition is required here.",
				Subject:  &ident.Range,
			},
		}
	}

	next := p.Peek()

	switch next.Type {
	case TokenEqual:
		return p.finishParsingBodyAttribute(ident)
	case TokenOQuote, TokenOBrace:
		return p.finishParsingBodyBlock(ident)
	default:
		p.recoverAfterBodyItem()
		return nil, zcl.Diagnostics{
			{
				Severity: zcl.DiagError,
				Summary:  "Attribute or block definition required",
				Detail:   "An attribute or block definition is required here. To define an attribute, use the equals sign \"=\" to introduce the attribute value.",
				Subject:  &ident.Range,
			},
		}
	}

	return nil, nil
}

func (p *parser) finishParsingBodyAttribute(ident Token) (Node, zcl.Diagnostics) {
	panic("attribute parsing not yet implemented")
}

func (p *parser) finishParsingBodyBlock(ident Token) (Node, zcl.Diagnostics) {
	var blockType = string(ident.Bytes)
	var diags zcl.Diagnostics
	var labels []string
	var labelRanges []zcl.Range

	var oBrace Token

Token:
	for {
		tok := p.Peek()

		switch tok.Type {

		case TokenOBrace:
			oBrace = p.Read()
			break Token

		case TokenOQuote:
			label, labelRange, labelDiags := p.parseQuotedStringLiteral()
			diags = append(diags, labelDiags...)
			labels = append(labels, label)
			labelRanges = append(labelRanges, labelRange)
			if labelDiags.HasErrors() {
				p.recoverAfterBodyItem()
				return &Block{
					Type:   blockType,
					Labels: labels,
					Body:   nil,

					TypeRange:       ident.Range,
					LabelRanges:     labelRanges,
					OpenBraceRange:  ident.Range, // placeholder
					CloseBraceRange: ident.Range, // placeholder
				}, diags
			}

		default:
			switch tok.Type {
			case TokenEqual:
				diags = append(diags, &zcl.Diagnostic{
					Severity: zcl.DiagError,
					Summary:  "Invalid block definition",
					Detail:   "The equals sign \"=\" indicates an attribute definition, and must not be used when defining a block.",
					Subject:  &tok.Range,
					Context:  zcl.RangeBetween(ident.Range, tok.Range).Ptr(),
				})
			case TokenNewline:
				diags = append(diags, &zcl.Diagnostic{
					Severity: zcl.DiagError,
					Summary:  "Invalid block definition",
					Detail:   "A block definition must have block content delimited by \"{\" and \"}\", starting on the same line as the block header.",
					Subject:  &tok.Range,
					Context:  zcl.RangeBetween(ident.Range, tok.Range).Ptr(),
				})
			default:
				if !p.recovery {
					diags = append(diags, &zcl.Diagnostic{
						Severity: zcl.DiagError,
						Summary:  "Invalid block definition",
						Detail:   "Either a quoted string block label or an opening brace (\"{\") is expected here.",
						Subject:  &tok.Range,
						Context:  zcl.RangeBetween(ident.Range, tok.Range).Ptr(),
					})
				}
			}

			p.recoverAfterBodyItem()

			return &Block{
				Type:   blockType,
				Labels: labels,
				Body:   nil,

				TypeRange:       ident.Range,
				LabelRanges:     labelRanges,
				OpenBraceRange:  ident.Range, // placeholder
				CloseBraceRange: ident.Range, // placeholder
			}, diags
		}
	}

	// Once we fall out here, the peeker is pointed just after our opening
	// brace, so we can begin our nested body parsing.
	body, bodyDiags := p.ParseBody(TokenCBrace)
	diags = append(diags, bodyDiags...)
	cBraceRange := p.PrevRange()

	return &Block{
		Type:   blockType,
		Labels: labels,
		Body:   body,

		TypeRange:       ident.Range,
		LabelRanges:     labelRanges,
		OpenBraceRange:  oBrace.Range,
		CloseBraceRange: cBraceRange,
	}, diags
}

// parseQuotedStringLiteral is a helper for parsing quoted strings that
// aren't allowed to contain any interpolations, such as block labels.
func (p *parser) parseQuotedStringLiteral() (string, zcl.Range, zcl.Diagnostics) {
	oQuote := p.Read()
	if oQuote.Type != TokenOQuote {
		return "", oQuote.Range, zcl.Diagnostics{
			{
				Severity: zcl.DiagError,
				Summary:  "Invalid string literal",
				Detail:   "A quoted string is required here.",
				Subject:  &oQuote.Range,
			},
		}
	}

	var diags zcl.Diagnostics
	ret := &bytes.Buffer{}
	var cQuote Token

Token:
	for {
		tok := p.Read()
		switch tok.Type {

		case TokenCQuote:
			cQuote = tok
			break Token

		case TokenStringLit:
			// TODO: Remove any escape sequences from the string, once we
			// have a function with which to do that.
			ret.Write(tok.Bytes)

		case TokenTemplateControl, TokenTemplateInterp:
			which := "$"
			if tok.Type == TokenTemplateControl {
				which = "!"
			}

			diags = append(diags, &zcl.Diagnostic{
				Severity: zcl.DiagError,
				Summary:  "Invalid string literal",
				Detail: fmt.Sprintf(
					"Template sequences are not allowed in this string. To include a literal %q, double it (as \"%s%s\") to escape it.",
					which, which, which,
				),
				Subject: &tok.Range,
				Context: zcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
			})
			p.recover(TokenTemplateSeqEnd)

		case TokenEOF:
			diags = append(diags, &zcl.Diagnostic{
				Severity: zcl.DiagError,
				Summary:  "Unterminated string literal",
				Detail:   "Unable to find the closing quote mark before the end of the file.",
				Subject:  &tok.Range,
				Context:  zcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
			})
			break Token

		default:
			// Should never happen, as long as the scanner is behaving itself
			diags = append(diags, &zcl.Diagnostic{
				Severity: zcl.DiagError,
				Summary:  "Invalid string literal",
				Detail:   "This item is not valid in a string literal.",
				Subject:  &tok.Range,
				Context:  zcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
			})
			p.recover(TokenOQuote)
			break Token

		}

	}

	return ret.String(), zcl.RangeBetween(oQuote.Range, cQuote.Range), diags
}

// recover seeks forward in the token stream until it finds TokenType "end",
// then returns with the peeker pointed at the following token.
//
// If the given token type is a bracketer, this function will additionally
// count nested instances of the brackets to try to leave the peeker at
// the end of the _current_ instance of that bracketer, skipping over any
// nested instances. This is a best-effort operation and may have
// unpredictable results on input with bad bracketer nesting.
func (p *parser) recover(end TokenType) {
	start := p.oppositeBracket(end)
	p.recovery = true

	nest := 0
	for {
		tok := p.Read()
		ty := tok.Type
		if end == TokenTemplateSeqEnd && ty == TokenTemplateControl {
			// normalize so that our matching behavior can work, since
			// TokenTemplateControl/TokenTemplateInterp are asymmetrical
			// with TokenTemplateSeqEnd and thus we need to count both
			// openers if that's the closer we're looking for.
			ty = TokenTemplateInterp
		}

		switch ty {
		case start:
			nest++
		case end:
			if nest < 1 {
				return
			}

			nest--
		case TokenEOF:
			return
		}
	}
}

// recoverOver seeks forward in the token stream until it finds a block
// starting with TokenType "start", then finds the corresponding end token,
// leaving the peeker pointed at the token after that end token.
//
// The given token type _must_ be a bracketer. For example, if the given
// start token is TokenOBrace then the parser will be left at the _end_ of
// the next brace-delimited block encountered, or at EOF if no such block
// is found or it is unclosed.
func (p *parser) recoverOver(start TokenType) {
	end := p.oppositeBracket(start)

	// find the opening bracket first
Token:
	for {
		tok := p.Read()
		switch tok.Type {
		case start, TokenEOF:
			break Token
		}
	}

	// Now use our existing recover function to locate the _end_ of the
	// container we've found.
	p.recover(end)
}

func (p *parser) recoverAfterBodyItem() {
	p.recovery = true
	var open []TokenType

Token:
	for {
		tok := p.Read()

		switch tok.Type {

		case TokenNewline:
			if len(open) == 0 {
				break Token
			}

		case TokenEOF:
			break Token

		case TokenOBrace, TokenOBrack, TokenOParen, TokenOQuote, TokenOHeredoc, TokenTemplateInterp, TokenTemplateControl:
			open = append(open, tok.Type)

		case TokenCBrace, TokenCBrack, TokenCParen, TokenCQuote, TokenCHeredoc:
			opener := p.oppositeBracket(tok.Type)
			for len(open) > 0 && open[len(open)-1] != opener {
				open = open[:len(open)-1]
			}
			if len(open) > 0 {
				open = open[:len(open)-1]
			}

		case TokenTemplateSeqEnd:
			for len(open) > 0 && open[len(open)-1] != TokenTemplateInterp && open[len(open)-1] != TokenTemplateControl {
				open = open[:len(open)-1]
			}
			if len(open) > 0 {
				open = open[:len(open)-1]
			}

		}
	}
}

// oppositeBracket finds the bracket that opposes the given bracketer, or
// NilToken if the given token isn't a bracketer.
//
// "Bracketer", for the sake of this function, is one end of a matching
// open/close set of tokens that establish a bracketing context.
func (p *parser) oppositeBracket(ty TokenType) TokenType {
	switch ty {

	case TokenOBrace:
		return TokenCBrace
	case TokenOBrack:
		return TokenCBrack
	case TokenOParen:
		return TokenCParen
	case TokenOQuote:
		return TokenCQuote
	case TokenOHeredoc:
		return TokenCHeredoc

	case TokenCBrace:
		return TokenOBrace
	case TokenCBrack:
		return TokenOBrack
	case TokenCParen:
		return TokenOParen
	case TokenCQuote:
		return TokenOQuote
	case TokenCHeredoc:
		return TokenOHeredoc

	case TokenTemplateControl:
		return TokenTemplateSeqEnd
	case TokenTemplateInterp:
		return TokenTemplateSeqEnd
	case TokenTemplateSeqEnd:
		// This is ambigous, but we return Interp here because that's
		// what's assumed by the "recover" method.
		return TokenTemplateInterp

	default:
		return TokenNil
	}
}
<|endoftext|>


<commit_before>package tmsh

// ClientSSLProffile contains information about Client SSL profile
type ClientSSLProfile struct {
	Name      string `ltm:"name"`
	Component string `ltm:"component"`

	Cert                string                       `ltm:"cert"`
	CertKeyChain        map[string]map[string]string `ltm:"cert-key-chain"`
	Chain               string                       `ltm:"chain"`
	DefaultsFrom        string                       `ltm:"defaults-from"`
	InheritCertkeychain bool                         `ltm:"inherit-certkeychain"`
	Key                 string                       `ltm:"key"`
}
<commit_msg>Add functions for Client SSL Profile<commit_after>package tmsh

import (
	"fmt"
	"strings"
)

// ClientSSLProffile contains information about Client SSL profile
type ClientSSLProfile struct {
	Name      string `ltm:"name"`
	Component string `ltm:"component"`

	Cert                string                       `ltm:"cert"`
	CertKeyChain        map[string]map[string]string `ltm:"cert-key-chain"`
	Chain               string                       `ltm:"chain"`
	DefaultsFrom        string                       `ltm:"defaults-from"`
	InheritCertkeychain bool                         `ltm:"inherit-certkeychain"`
	Key                 string                       `ltm:"key"`
}

// GetAllClientSSLProfiles returns a list of all Client SSL Profiles
func (bigip *BigIP) GetAllClientSSLProfiles() ([]ClientSSLProfile, error) {
	ret, err := bigip.ExecuteCommand("list ltm profile client-ssl")
	if err != nil {
		return nil, err
	}

	var profs []ClientSSLProfile
	for _, p := range splitLtmOutput(ret) {
		var prof ClientSSLProfile
		if err := Unmarshal(p, &prof); err != nil {
			return nil, err
		}
		profs = append(profs, prof)
	}

	return profs, nil
}

// GetClientSSLProfile gets a Client SSL Profile by name. Return nil if the profile does not found.
func (bigip *BigIP) GetClientSSLProfile(name string) (*ClientSSLProfile, error) {
	ret, _ := bigip.ExecuteCommand("list ltm profile client-ssl " + name)
	if strings.Contains(ret, "was not found.") {
		return nil, fmt.Errorf(ret)
	}

	var prof ClientSSLProfile
	if err := Unmarshal(ret, &prof); err != nil {
		return nil, err
	}

	return &prof, nil
}
<|endoftext|>


<commit_before>package enroll

import (
	"github.com/satori/go.uuid"
	"time"
)

type Payload struct {
	PayloadType         string      `json:"type" db:"type"`
	PayloadVersion      int         `json:"version" db:"version"`
	PayloadIdentifier   string      `json:"identifier" db:"identifier"`
	PayloadUUID         string      `json:"uuid" db:"uuid"`
	PayloadDisplayName  string      `json:"displayname" db:"displayname"`
	PayloadDescription  string      `json:"description,omitempty" db:"description"`
	PayloadOrganization string      `json:"organization,omitempty" db:"organization"`
	PayloadScope        string      `json:"scope" db:"scope" plist:"omitempty"`
	PayloadContent      interface{} `json:"content,omitempty" plist:"PayloadContent,omitempty"`
}

type Profile struct {
	PayloadContent           []interface{}     `json:"content,omitempty" db:"content"`
	PayloadDescription       string            `json:"description,omitempty" db:"description"`
	PayloadDisplayName       string            `json:"displayname,omitempty" db:"displayname"`
	PayloadExpirationDate    *time.Time        `json:"expiration_date,omitempty" db:"expiration_date" plist:"omitempty"`
	PayloadIdentifier        string            `json:"identifier" db:"identifier"`
	PayloadOrganization      string            `json:"organization,omitempty" db:"organization"`
	PayloadUUID              string            `json:"uuid" db:"uuid"`
	PayloadRemovalDisallowed bool              `json:"removal_disallowed" db:"removal_disallowed" plist:"omitempty"`
	PayloadType              string            `json:"type" db:"type"`
	PayloadVersion           int               `json:"version" db:"version"`
	PayloadScope             string            `json:"scope" db:"scope" plist:"omitempty"`
	RemovalDate              *time.Time        `json:"removal_date" db:"removal_date" plist:"-" plist:"omitempty"`
	DurationUntilRemoval     float32           `json:"duration_until_removal" db:"duration_until_removal" plist:"omitempty"`
	ConsentText              map[string]string `json:"consent_text" db:"consent_text" plist:"omitempty"`
}

func NewProfile() *Profile {
	payloadUuid := uuid.NewV4()

	return &Profile{
		PayloadVersion: 1,
		PayloadType:    "Configuration",
		PayloadUUID:    payloadUuid.String(),
	}
}

func NewPayload(payloadType string) *Payload {
	payloadUuid := uuid.NewV4()

	return &Payload{
		PayloadVersion: 1,
		PayloadType:    payloadType,
		PayloadUUID:    payloadUuid.String(),
	}
}

type SCEPPayloadContent struct {
	CAFingerprint []byte `plist:"CAFingerprint,omitempty"` // NSData
	Challenge     string `plist:"Challenge,omitempty"`
	Keysize       int
	KeyType       string `plist:"Key Type"`
	KeyUsage      int    `plist:"Key Usage"`
	Name          string
	Subject       [][][]string `plist:"Subject,omitempty"`
	URL           string
}

// TODO: Actually this is one of those non-nested payloads that doesnt respect the PayloadContent key.
type MDMPayloadContent struct {
	Payload
	AccessRights            int
	CheckInURL              string
	CheckOutWhenRemoved     bool
	IdentityCertificateUUID string
	ServerCapabilities      []string `plist:"ServerCapabilities,omitempty"`
	ServerURL               string
	Topic                   string
}
<commit_msg>Hotfix/enroll payloadscope (#31)<commit_after>package enroll

import (
	"github.com/satori/go.uuid"
	"time"
)

type Payload struct {
	PayloadType         string      `json:"type" db:"type"`
	PayloadVersion      int         `json:"version" db:"version"`
	PayloadIdentifier   string      `json:"identifier" db:"identifier"`
	PayloadUUID         string      `json:"uuid" db:"uuid"`
	PayloadDisplayName  string      `json:"displayname" db:"displayname"`
	PayloadDescription  string      `json:"description,omitempty" db:"description"`
	PayloadOrganization string      `json:"organization,omitempty" db:"organization"`
	PayloadScope        string      `json:"scope" db:"scope" plist:",omitempty"`
	PayloadContent      interface{} `json:"content,omitempty" plist:"PayloadContent,omitempty"`
}

type Profile struct {
	PayloadContent           []interface{}     `json:"content,omitempty" db:"content"`
	PayloadDescription       string            `json:"description,omitempty" db:"description"`
	PayloadDisplayName       string            `json:"displayname,omitempty" db:"displayname"`
	PayloadExpirationDate    *time.Time        `json:"expiration_date,omitempty" db:"expiration_date" plist:",omitempty"`
	PayloadIdentifier        string            `json:"identifier" db:"identifier"`
	PayloadOrganization      string            `json:"organization,omitempty" db:"organization"`
	PayloadUUID              string            `json:"uuid" db:"uuid"`
	PayloadRemovalDisallowed bool              `json:"removal_disallowed" db:"removal_disallowed" plist:",omitempty"`
	PayloadType              string            `json:"type" db:"type"`
	PayloadVersion           int               `json:"version" db:"version"`
	PayloadScope             string            `json:"scope" db:"scope" plist:",omitempty"`
	RemovalDate              *time.Time        `json:"removal_date" db:"removal_date" plist:"-" plist:",omitempty"`
	DurationUntilRemoval     float32           `json:"duration_until_removal" db:"duration_until_removal" plist:",omitempty"`
	ConsentText              map[string]string `json:"consent_text" db:"consent_text" plist:"omitempty"`
}

func NewProfile() *Profile {
	payloadUuid := uuid.NewV4()

	return &Profile{
		PayloadVersion: 1,
		PayloadType:    "Configuration",
		PayloadUUID:    payloadUuid.String(),
	}
}

func NewPayload(payloadType string) *Payload {
	payloadUuid := uuid.NewV4()

	return &Payload{
		PayloadVersion: 1,
		PayloadType:    payloadType,
		PayloadUUID:    payloadUuid.String(),
	}
}

type SCEPPayloadContent struct {
	CAFingerprint []byte `plist:"CAFingerprint,omitempty"` // NSData
	Challenge     string `plist:"Challenge,omitempty"`
	Keysize       int
	KeyType       string `plist:"Key Type"`
	KeyUsage      int    `plist:"Key Usage"`
	Name          string
	Subject       [][][]string `plist:"Subject,omitempty"`
	URL           string
}

// TODO: Actually this is one of those non-nested payloads that doesnt respect the PayloadContent key.
type MDMPayloadContent struct {
	Payload
	AccessRights            int
	CheckInURL              string
	CheckOutWhenRemoved     bool
	IdentityCertificateUUID string
	ServerCapabilities      []string `plist:"ServerCapabilities,omitempty"`
	ServerURL               string
	Topic                   string
}
<|endoftext|>