Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

many: support mixed outcomes for permissions in prompting constraints #14581

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
20 changes: 7 additions & 13 deletions daemon/api_prompting.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,19 +283,16 @@ var getInterfaceManager = func(c *Command) interfaceManager {
}

type postPromptBody struct {
Outcome prompting.OutcomeType `json:"action"`
Lifespan prompting.LifespanType `json:"lifespan"`
Duration string `json:"duration,omitempty"`
Constraints *prompting.Constraints `json:"constraints"`
Outcome prompting.OutcomeType `json:"action"`
Lifespan prompting.LifespanType `json:"lifespan"`
Duration string `json:"duration,omitempty"`
Constraints *prompting.ReplyConstraints `json:"constraints"`
}

type addRuleContents struct {
Snap string `json:"snap"`
Interface string `json:"interface"`
Constraints *prompting.Constraints `json:"constraints"`
Outcome prompting.OutcomeType `json:"outcome"`
Lifespan prompting.LifespanType `json:"lifespan"`
Duration string `json:"duration,omitempty"`
}

type removeRulesSelector struct {
Expand All @@ -304,10 +301,7 @@ type removeRulesSelector struct {
}

type patchRuleContents struct {
Constraints *prompting.Constraints `json:"constraints,omitempty"`
Outcome prompting.OutcomeType `json:"outcome,omitempty"`
Lifespan prompting.LifespanType `json:"lifespan,omitempty"`
Duration string `json:"duration,omitempty"`
Constraints *prompting.PatchConstraints `json:"constraints,omitempty"`
}

type postRulesRequestBody struct {
Expand Down Expand Up @@ -452,7 +446,7 @@ func postRules(c *Command, r *http.Request, user *auth.UserState) Response {
if postBody.AddRule == nil {
return BadRequest(`must include "rule" field in request body when action is "add"`)
}
newRule, err := getInterfaceManager(c).InterfacesRequestsManager().AddRule(userID, postBody.AddRule.Snap, postBody.AddRule.Interface, postBody.AddRule.Constraints, postBody.AddRule.Outcome, postBody.AddRule.Lifespan, postBody.AddRule.Duration)
newRule, err := getInterfaceManager(c).InterfacesRequestsManager().AddRule(userID, postBody.AddRule.Snap, postBody.AddRule.Interface, postBody.AddRule.Constraints)
if err != nil {
return promptingError(err)
}
Expand Down Expand Up @@ -529,7 +523,7 @@ func postRule(c *Command, r *http.Request, user *auth.UserState) Response {
if postBody.PatchRule == nil {
return BadRequest(`must include "rule" field in request body when action is "patch"`)
}
patchedRule, err := getInterfaceManager(c).InterfacesRequestsManager().PatchRule(userID, ruleID, postBody.PatchRule.Constraints, postBody.PatchRule.Outcome, postBody.PatchRule.Lifespan, postBody.PatchRule.Duration)
patchedRule, err := getInterfaceManager(c).InterfacesRequestsManager().PatchRule(userID, ruleID, postBody.PatchRule.Constraints)
if err != nil {
return promptingError(err)
}
Expand Down
180 changes: 103 additions & 77 deletions daemon/api_prompting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,16 @@ type fakeInterfacesRequestsManager struct {
err error

// Store most recent received values
userID uint32
snap string
iface string
id prompting.IDType // used for prompt ID or rule ID
constraints *prompting.Constraints
outcome prompting.OutcomeType
lifespan prompting.LifespanType
duration string
userID uint32
snap string
iface string
id prompting.IDType // used for prompt ID or rule ID
ruleConstraints *prompting.Constraints
patchConstraints *prompting.PatchConstraints
replyConstraints *prompting.ReplyConstraints
outcome prompting.OutcomeType
lifespan prompting.LifespanType
duration string
}

func (m *fakeInterfacesRequestsManager) Prompts(userID uint32) ([]*requestprompts.Prompt, error) {
Expand All @@ -73,10 +75,10 @@ func (m *fakeInterfacesRequestsManager) PromptWithID(userID uint32, promptID pro
return m.prompt, m.err
}

func (m *fakeInterfacesRequestsManager) HandleReply(userID uint32, promptID prompting.IDType, constraints *prompting.Constraints, outcome prompting.OutcomeType, lifespan prompting.LifespanType, duration string) ([]prompting.IDType, error) {
func (m *fakeInterfacesRequestsManager) HandleReply(userID uint32, promptID prompting.IDType, constraints *prompting.ReplyConstraints, outcome prompting.OutcomeType, lifespan prompting.LifespanType, duration string) ([]prompting.IDType, error) {
m.userID = userID
m.id = promptID
m.constraints = constraints
m.replyConstraints = constraints
m.outcome = outcome
m.lifespan = lifespan
m.duration = duration
Expand All @@ -90,14 +92,11 @@ func (m *fakeInterfacesRequestsManager) Rules(userID uint32, snap string, iface
return m.rules, m.err
}

func (m *fakeInterfacesRequestsManager) AddRule(userID uint32, snap string, iface string, constraints *prompting.Constraints, outcome prompting.OutcomeType, lifespan prompting.LifespanType, duration string) (*requestrules.Rule, error) {
func (m *fakeInterfacesRequestsManager) AddRule(userID uint32, snap string, iface string, constraints *prompting.Constraints) (*requestrules.Rule, error) {
m.userID = userID
m.snap = snap
m.iface = iface
m.constraints = constraints
m.outcome = outcome
m.lifespan = lifespan
m.duration = duration
m.ruleConstraints = constraints
return m.rule, m.err
}

Expand All @@ -114,13 +113,10 @@ func (m *fakeInterfacesRequestsManager) RuleWithID(userID uint32, ruleID prompti
return m.rule, m.err
}

func (m *fakeInterfacesRequestsManager) PatchRule(userID uint32, ruleID prompting.IDType, constraints *prompting.Constraints, outcome prompting.OutcomeType, lifespan prompting.LifespanType, duration string) (*requestrules.Rule, error) {
func (m *fakeInterfacesRequestsManager) PatchRule(userID uint32, ruleID prompting.IDType, constraints *prompting.PatchConstraints) (*requestrules.Rule, error) {
m.userID = userID
m.id = ruleID
m.constraints = constraints
m.outcome = outcome
m.lifespan = lifespan
m.duration = duration
m.patchConstraints = constraints
return m.rule, m.err
}

Expand Down Expand Up @@ -700,7 +696,7 @@ func (s *promptingSuite) TestPostPromptHappy(c *C) {
prompting.IDType(0xF00BA4),
}

constraints := &prompting.Constraints{
constraints := &prompting.ReplyConstraints{
PathPattern: mustParsePathPattern(c, "/home/test/Pictures/**/*.{png,svg}"),
Permissions: []string{"read", "execute"},
}
Expand All @@ -718,7 +714,7 @@ func (s *promptingSuite) TestPostPromptHappy(c *C) {
// Check parameters
c.Check(s.manager.userID, Equals, uint32(1000))
c.Check(s.manager.id, Equals, prompting.IDType(0x0123456789abcdef))
c.Check(s.manager.constraints, DeepEquals, contents.Constraints)
c.Check(s.manager.replyConstraints, DeepEquals, contents.Constraints)
c.Check(s.manager.outcome, Equals, contents.Outcome)
c.Check(s.manager.lifespan, Equals, contents.Lifespan)
c.Check(s.manager.duration, Equals, contents.Duration)
Expand Down Expand Up @@ -775,13 +771,15 @@ func (s *promptingSuite) TestGetRulesHappy(c *C) {
User: 1234,
Snap: "firefox",
Interface: "home",
Constraints: &prompting.Constraints{
Constraints: &prompting.RuleConstraints{
PathPattern: mustParsePathPattern(c, "/foo/bar"),
Permissions: []string{"write"},
Permissions: prompting.RulePermissionMap{
"write": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeDeny,
Lifespan: prompting.LifespanForever,
},
},
},
Outcome: prompting.OutcomeDeny,
Lifespan: prompting.LifespanForever,
Expiration: time.Now(),
},
}

Expand Down Expand Up @@ -810,26 +808,34 @@ func (s *promptingSuite) TestPostRulesAddHappy(c *C) {
User: 11235,
Snap: "firefox",
Interface: "home",
Constraints: &prompting.Constraints{
Constraints: &prompting.RuleConstraints{
PathPattern: mustParsePathPattern(c, "/foo/bar/baz"),
Permissions: []string{"write"},
Permissions: prompting.RulePermissionMap{
"write": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeDeny,
Lifespan: prompting.LifespanForever,
},
},
},
Outcome: prompting.OutcomeDeny,
Lifespan: prompting.LifespanForever,
Expiration: time.Now(),
}

constraints := &prompting.Constraints{
PathPattern: mustParsePathPattern(c, "/home/test/{foo,bar,baz}/**/*.{png,svg}"),
Permissions: []string{"read", "write"},
Permissions: prompting.PermissionMap{
"read": &prompting.PermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
"write": &prompting.PermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
},
}
contents := &daemon.AddRuleContents{
Snap: "thunderbird",
Interface: "home",
Constraints: constraints,
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
Duration: "",
}
postBody := &daemon.PostRulesRequestBody{
Action: "add",
Expand All @@ -844,10 +850,7 @@ func (s *promptingSuite) TestPostRulesAddHappy(c *C) {
c.Check(s.manager.userID, Equals, uint32(11235))
c.Check(s.manager.snap, Equals, contents.Snap)
c.Check(s.manager.iface, Equals, contents.Interface)
c.Check(s.manager.constraints, DeepEquals, contents.Constraints)
c.Check(s.manager.outcome, Equals, contents.Outcome)
c.Check(s.manager.lifespan, Equals, contents.Lifespan)
c.Check(s.manager.duration, Equals, contents.Duration)
c.Check(s.manager.ruleConstraints, DeepEquals, contents.Constraints)

// Check return value
rule, ok := rsp.Result.(*requestrules.Rule)
Expand Down Expand Up @@ -881,35 +884,42 @@ func (s *promptingSuite) TestPostRulesRemoveHappy(c *C) {
s.manager = &fakeInterfacesRequestsManager{}

// Set the rules to return
var timeZero time.Time
s.manager.rules = []*requestrules.Rule{
{
ID: prompting.IDType(1234),
Timestamp: time.Now(),
User: 1001,
Snap: "thunderird",
Interface: "home",
Constraints: &prompting.Constraints{
Constraints: &prompting.RuleConstraints{
PathPattern: mustParsePathPattern(c, "/foo/bar/baz/qux"),
Permissions: []string{"write"},
Permissions: prompting.RulePermissionMap{
"write": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeDeny,
Lifespan: prompting.LifespanForever,
},
},
},
Outcome: prompting.OutcomeDeny,
Lifespan: prompting.LifespanForever,
Expiration: timeZero,
},
{
ID: prompting.IDType(5678),
Timestamp: time.Now(),
User: 1001,
Snap: "thunderbird",
Interface: "home",
Constraints: &prompting.Constraints{
Constraints: &prompting.RuleConstraints{
PathPattern: mustParsePathPattern(c, "/fizz/buzz"),
Permissions: []string{"read", "execute"},
Permissions: prompting.RulePermissionMap{
"read": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanTimespan,
},
"execute": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanTimespan,
},
},
},
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanTimespan,
Expiration: time.Now(),
},
}

Expand Down Expand Up @@ -948,13 +958,16 @@ func (s *promptingSuite) TestGetRuleHappy(c *C) {
User: 1005,
Snap: "thunderbird",
Interface: "home",
Constraints: &prompting.Constraints{
Constraints: &prompting.RuleConstraints{
PathPattern: mustParsePathPattern(c, "/home/test/Videos/**/*.{mkv,mp4,mov}"),
Permissions: []string{"read"},
Permissions: prompting.RulePermissionMap{
"read": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanTimespan,
Expiration: time.Now().Add(-24 * time.Hour),
},
},
},
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanTimespan,
Expiration: time.Now().Add(-24 * time.Hour),
}

rsp := s.makeSyncReq(c, "GET", "/v2/interfaces/requests/rules/000000000000012B", 1005, nil)
Expand All @@ -974,31 +987,42 @@ func (s *promptingSuite) TestPostRulePatchHappy(c *C) {

s.daemon(c)

var timeZero time.Time
s.manager.rule = &requestrules.Rule{
ID: prompting.IDType(0x01123581321),
Timestamp: time.Now(),
User: 999,
Snap: "gimp",
Interface: "home",
Constraints: &prompting.Constraints{
Constraints: &prompting.RuleConstraints{
PathPattern: mustParsePathPattern(c, "/home/test/Pictures/**/*.{png,jpg}"),
Permissions: []string{"read", "write"},
Permissions: prompting.RulePermissionMap{
"read": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
"write": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
},
},
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
Expiration: timeZero,
}

constraints := &prompting.Constraints{
constraints := &prompting.PatchConstraints{
PathPattern: mustParsePathPattern(c, "/home/test/Pictures/**/*.{png,jpg}"),
Permissions: []string{"read", "write"},
Permissions: prompting.PermissionMap{
"read": &prompting.PermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
"write": &prompting.PermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
},
}
contents := &daemon.PatchRuleContents{
Constraints: constraints,
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
Duration: "",
}
postBody := &daemon.PostRuleRequestBody{
Action: "patch",
Expand All @@ -1011,10 +1035,7 @@ func (s *promptingSuite) TestPostRulePatchHappy(c *C) {

// Check parameters
c.Check(s.manager.userID, Equals, uint32(999))
c.Check(s.manager.constraints, DeepEquals, contents.Constraints)
c.Check(s.manager.outcome, Equals, contents.Outcome)
c.Check(s.manager.lifespan, Equals, contents.Lifespan)
c.Check(s.manager.duration, Equals, contents.Duration)
c.Check(s.manager.patchConstraints, DeepEquals, contents.Constraints)

// Check return value
rule, ok := rsp.Result.(*requestrules.Rule)
Expand All @@ -1027,20 +1048,25 @@ func (s *promptingSuite) TestPostRuleRemoveHappy(c *C) {

s.daemon(c)

var timeZero time.Time
s.manager.rule = &requestrules.Rule{
ID: prompting.IDType(0x01123581321),
Timestamp: time.Now(),
User: 100,
Snap: "gimp",
Interface: "home",
Constraints: &prompting.Constraints{
Constraints: &prompting.RuleConstraints{
PathPattern: mustParsePathPattern(c, "/home/test/Pictures/**/*.{png,jpg}"),
Permissions: []string{"read", "write"},
Permissions: prompting.RulePermissionMap{
"read": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
"write": &prompting.RulePermissionEntry{
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
},
},
},
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
Expiration: timeZero,
}
postBody := &daemon.PostRuleRequestBody{
Action: "remove",
Expand Down
Loading
Loading