github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/usersession/agent/session_agent_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package agent_test 21 22 import ( 23 "encoding/json" 24 "fmt" 25 "net" 26 "net/http" 27 "os" 28 "syscall" 29 "testing" 30 "time" 31 32 . "gopkg.in/check.v1" 33 34 "github.com/snapcore/snapd/dirs" 35 "github.com/snapcore/snapd/logger" 36 "github.com/snapcore/snapd/osutil/sys" 37 "github.com/snapcore/snapd/testutil" 38 "github.com/snapcore/snapd/usersession/agent" 39 ) 40 41 func Test(t *testing.T) { TestingT(t) } 42 43 type sessionAgentSuite struct { 44 testutil.DBusTest 45 socketPath string 46 client *http.Client 47 } 48 49 var _ = Suite(&sessionAgentSuite{}) 50 51 func (s *sessionAgentSuite) SetUpTest(c *C) { 52 s.DBusTest.SetUpTest(c) 53 dirs.SetRootDir(c.MkDir()) 54 xdgRuntimeDir := fmt.Sprintf("%s/%d", dirs.XdgRuntimeDirBase, os.Getuid()) 55 c.Assert(os.MkdirAll(xdgRuntimeDir, 0700), IsNil) 56 s.socketPath = fmt.Sprintf("%s/snapd-session-agent.socket", xdgRuntimeDir) 57 58 transport := &http.Transport{ 59 Dial: func(_, _ string) (net.Conn, error) { 60 return net.Dial("unix", s.socketPath) 61 }, 62 DisableKeepAlives: true, 63 } 64 s.client = &http.Client{Transport: transport} 65 } 66 67 func (s *sessionAgentSuite) TearDownTest(c *C) { 68 dirs.SetRootDir("") 69 logger.SetLogger(logger.NullLogger) 70 s.DBusTest.TearDownTest(c) 71 } 72 73 func (s *sessionAgentSuite) TestStartStop(c *C) { 74 agent, err := agent.New() 75 c.Assert(err, IsNil) 76 agent.Version = "42" 77 agent.Start() 78 defer func() { c.Check(agent.Stop(), IsNil) }() 79 80 // The agent has connected to the session bus 81 var hasOwner bool 82 c.Check(s.DBusTest.SessionBus.BusObject().Call("org.freedesktop.DBus.NameHasOwner", 0, "io.snapcraft.SessionAgent").Store(&hasOwner), IsNil) 83 c.Check(hasOwner, Equals, true) 84 85 // The agent is listening for REST API requests 86 response, err := s.client.Get("http://localhost/v1/session-info") 87 c.Assert(err, IsNil) 88 defer response.Body.Close() 89 c.Check(response.StatusCode, Equals, 200) 90 91 var rst struct { 92 Result struct { 93 Version string `json:"version"` 94 } `json:"result"` 95 } 96 c.Assert(json.NewDecoder(response.Body).Decode(&rst), IsNil) 97 c.Check(rst.Result.Version, Equals, "42") 98 response.Body.Close() 99 100 c.Check(agent.Stop(), IsNil) 101 } 102 103 func (s *sessionAgentSuite) TestDying(c *C) { 104 agent, err := agent.New() 105 c.Assert(err, IsNil) 106 agent.Start() 107 select { 108 case <-agent.Dying(): 109 c.Error("agent.Dying() channel closed prematurely") 110 default: 111 } 112 go func() { 113 time.Sleep(5 * time.Millisecond) 114 c.Check(agent.Stop(), IsNil) 115 }() 116 select { 117 case <-agent.Dying(): 118 case <-time.After(2 * time.Second): 119 c.Error("agent.Dying() channel was not closed when agent stopped") 120 } 121 } 122 123 func (s *sessionAgentSuite) TestExitOnIdle(c *C) { 124 agent, err := agent.New() 125 c.Assert(err, IsNil) 126 agent.IdleTimeout = 150 * time.Millisecond 127 startTime := time.Now() 128 agent.Start() 129 defer agent.Stop() 130 131 makeRequest := func() { 132 response, err := s.client.Get("http://localhost/v1/session-info") 133 c.Assert(err, IsNil) 134 defer response.Body.Close() 135 c.Check(response.StatusCode, Equals, 200) 136 } 137 makeRequest() 138 time.Sleep(25 * time.Millisecond) 139 makeRequest() 140 141 select { 142 case <-agent.Dying(): 143 case <-time.After(2 * time.Second): 144 c.Fatal("agent did not exit after idle timeout expired") 145 } 146 elapsed := time.Since(startTime) 147 if elapsed < 175*time.Millisecond || elapsed > 450*time.Millisecond { 148 // The idle timeout should have been extended when we 149 // issued a second request after 25ms. 150 c.Errorf("Expected ellaped time close to 175 ms, but got %v", elapsed) 151 } 152 } 153 154 func (s *sessionAgentSuite) TestConnectFromOtherUser(c *C) { 155 logbuf, restore := logger.MockLogger() 156 defer restore() 157 158 // Mock connections to appear to come from a different user ID 159 uid := uint32(sys.Geteuid()) 160 restore = agent.MockUcred(&syscall.Ucred{Uid: uid + 1}, nil) 161 defer restore() 162 163 sa, err := agent.New() 164 c.Assert(err, IsNil) 165 sa.Start() 166 defer sa.Stop() 167 168 _, err = s.client.Get("http://localhost/v1/session-info") 169 // This could be an EOF error or a failed read, depending on timing 170 c.Assert(err, ErrorMatches, "Get \"?http://localhost/v1/session-info\"?: .*") 171 logger.WithLoggerLock(func() { 172 c.Check(logbuf.String(), testutil.Contains, "Blocking request from user ID") 173 }) 174 } 175 176 func (s *sessionAgentSuite) TestConnectFromRoot(c *C) { 177 logbuf, restore := logger.MockLogger() 178 defer restore() 179 180 // Mock connections to appear to come from root 181 restore = agent.MockUcred(&syscall.Ucred{Uid: 0}, nil) 182 defer restore() 183 184 sa, err := agent.New() 185 c.Assert(err, IsNil) 186 sa.Start() 187 defer sa.Stop() 188 189 response, err := s.client.Get("http://localhost/v1/session-info") 190 c.Assert(err, IsNil) 191 defer response.Body.Close() 192 c.Check(response.StatusCode, Equals, 200) 193 logger.WithLoggerLock(func() { 194 c.Check(logbuf.String(), Equals, "") 195 }) 196 } 197 198 func (s *sessionAgentSuite) TestConnectWithFailedPeerCredentials(c *C) { 199 logbuf, restore := logger.MockLogger() 200 defer restore() 201 202 // Connections are dropped if peer credential lookup fails. 203 restore = agent.MockUcred(nil, fmt.Errorf("SO_PEERCRED failed")) 204 defer restore() 205 206 sa, err := agent.New() 207 c.Assert(err, IsNil) 208 sa.Start() 209 defer sa.Stop() 210 211 _, err = s.client.Get("http://localhost/v1/session-info") 212 c.Assert(err, ErrorMatches, "Get \"?http://localhost/v1/session-info\"?: .*") 213 logger.WithLoggerLock(func() { 214 c.Check(logbuf.String(), testutil.Contains, "Failed to retrieve peer credentials: SO_PEERCRED failed") 215 }) 216 }