{"action":"created","comment":{"url":"https://api.github.com/repos/influxdata/chronograf/pulls/comments/148534696","pull_request_review_id":73790117,"id":148534696,"diff_hunk":"@@ -0,0 +1,143 @@\n+package bolt\n+\n+import (\n+\t\"context\"\n+\t\"fmt\"\n+\n+\t\"github.com/boltdb/bolt\"\n+\t\"github.com/influxdata/chronograf\"\n+\t\"github.com/influxdata/chronograf/bolt/internal\"\n+)\n+\n+// Ensure OrganizationsStore implements chronograf.OrganizationsStore.\n+var _ chronograf.OrganizationsStore = &OrganizationsStore{}\n+\n+// OrganizationsStore is used to store organizations local to chronograf\n+var OrganizationsBucket = []byte(\"OrganizationsV1\")\n+\n+// OrganizationsStore uses bolt to store and retrieve Organizations\n+type OrganizationsStore struct {\n+\tclient *Client\n+}\n+\n+func (s *OrganizationsStore) Add(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) {\n+\tif err := s.client.db.Update(func(tx *bolt.Tx) error {\n+\t\tb := tx.Bucket(OrganizationsBucket)\n+\t\tseq, err := b.NextSequence()\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\to.ID = seq\n+\t\tif v, err := internal.MarshalOrganization(o); err != nil {\n+\t\t\treturn err\n+\t\t} else if err := b.Put(u64tob(seq), v); err != nil {\n+\t\t\treturn err\n+\t\t}\n+\n+\t\treturn nil\n+\t}); err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\treturn o, nil\n+}\n+\n+func (s *OrganizationsStore) All(ctx context.Context) ([]chronograf.Organization, error) {\n+\tvar orgs []chronograf.Organization\n+\terr := s.each(ctx, func(o *chronograf.Organization) {\n+\t\torgs = append(orgs, *o)\n+\t})\n+\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\treturn orgs, nil\n+}\n+\n+func (s *OrganizationsStore) Delete(ctx context.Context, o *chronograf.Organization) error {\n+\t_, err := s.get(ctx, o.ID)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\treturn s.client.db.Update(func(tx *bolt.Tx) error {\n+\t\treturn tx.Bucket(OrganizationsBucket).Delete(u64tob(o.ID))\n+\t})\n+}\n+\n+func (s *OrganizationsStore) get(ctx context.Context, id uint64) (*chronograf.Organization, error) {\n+\tvar o chronograf.Organization\n+\terr := s.client.db.View(func(tx *bolt.Tx) error {\n+\t\tv := tx.Bucket(OrganizationsBucket).Get(u64tob(id))\n+\t\tif v == nil {\n+\t\t\treturn chronograf.ErrOrganizationNotFound\n+\t\t}\n+\t\treturn internal.UnmarshalOrganization(v, &o)\n+\t})\n+\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\treturn &o, nil\n+}\n+\n+func (s *OrganizationsStore) each(ctx context.Context, fn func(*chronograf.Organization)) error {\n+\treturn s.client.db.View(func(tx *bolt.Tx) error {\n+\t\treturn tx.Bucket(OrganizationsBucket).ForEach(func(k, v []byte) error {\n+\t\t\tvar org chronograf.Organization\n+\t\t\tif err := internal.UnmarshalOrganization(v, &org); err != nil {\n+\t\t\t\treturn err\n+\t\t\t}\n+\t\t\tfn(&org)\n+\t\t\treturn nil\n+\t\t})\n+\t})\n+\treturn nil\n+}\n+\n+func (s *OrganizationsStore) Get(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {\n+\tif q.ID != nil {\n+\t\treturn s.get(ctx, *q.ID)\n+\t}\n+\n+\tif q.Name != nil {\n+\t\tvar org *chronograf.Organization\n+\t\terr := s.each(ctx, func(o *chronograf.Organization) {","path":"bolt/organizations.go","position":202,"original_position":106,"commit_id":"78754bdbdab8fc2e407b34b464edeb1316da298e","original_commit_id":"d7d69730d7e19b44a7e57b5020a47f538b9de094","user":{"login":"desa","id":1477539,"avatar_url":"https://avatars1.githubusercontent.com/u/1477539?v=4","gravatar_id":"","url":"https://api.github.com/users/desa","html_url":"https://github.com/desa","followers_url":"https://api.github.com/users/desa/followers","following_url":"https://api.github.com/users/desa/following{/other_user}","gists_url":"https://api.github.com/users/desa/gists{/gist_id}","starred_url":"https://api.github.com/users/desa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/desa/subscriptions","organizations_url":"https://api.github.com/users/desa/orgs","repos_url":"https://api.github.com/users/desa/repos","events_url":"https://api.github.com/users/desa/events{/privacy}","received_events_url":"https://api.github.com/users/desa/received_events","type":"User","site_admin":false},"body":"@goller added comment in the `Get` method, do you think there should be one here as well?","created_at":"2017-11-02T13:40:28Z","updated_at":"2017-11-02T13:40:29Z","html_url":"https://github.com/influxdata/chronograf/pull/2142#discussion_r148534696","pull_request_url":"https://api.github.com/repos/influxdata/chronograf/pulls/2142","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/influxdata/chronograf/pulls/comments/148534696"},"html":{"href":"https://github.com/influxdata/chronograf/pull/2142#discussion_r148534696"},"pull_request":{"href":"https://api.github.com/repos/influxdata/chronograf/pulls/2142"}},"in_reply_to_id":148027511},"pull_request":{"url":"https://api.github.com/repos/influxdata/chronograf/pulls/2142","id":147859602,"html_url":"https://github.com/influxdata/chronograf/pull/2142","diff_url":"https://github.com/influxdata/chronograf/pull/2142.diff","patch_url":"https://github.com/influxdata/chronograf/pull/2142.patch","issue_url":"https://api.github.com/repos/influxdata/chronograf/issues/2142","number":2142,"state":"open","locked":false,"title":"Implement Organizations","user":{"login":"desa","id":1477539,"avatar_url":"https://avatars1.githubusercontent.com/u/1477539?v=4","gravatar_id":"","url":"https://api.github.com/users/desa","html_url":"https://github.com/desa","followers_url":"https://api.github.com/users/desa/followers","following_url":"https://api.github.com/users/desa/following{/other_user}","gists_url":"https://api.github.com/users/desa/gists{/gist_id}","starred_url":"https://api.github.com/users/desa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/desa/subscriptions","organizations_url":"https://api.github.com/users/desa/orgs","repos_url":"https://api.github.com/users/desa/repos","events_url":"https://api.github.com/users/desa/events{/privacy}","received_events_url":"https://api.github.com/users/desa/received_events","type":"User","site_admin":false},"body":"  - [x] Rebased/mergable\r\n  - [x] Tests pass\r\n\r\nConnect #1909 \r\nConnect #1910\r\nConnect #1911 \r\nConnect #1912  \r\nConnect #1927 \r\nConnect #2173 \r\nConnect #2182 \r\nConnect #2192 \r\nConnect #2194 \r\nConnect #2197\r\n\r\n### WIP TODO\r\nSection 1\r\n- [x] Add Routes for Org CRUD\r\n- [x] Add Tests for Routes for Org CRUD\r\n- [x] Add UsersRole \"join\" table to retrieve a users roles within an organization.\r\n- [x] Add Organizations logic to Users\r\n- [x] disallow update OrganizationID on a role\r\n\r\nSection 2\r\n- [x] make singular/plural consistent across bucket & store names, in and outside of Organization(s)\r\n  - e.g. `orgDashboardBucket`, `LayoutStore`\r\n- [x] add OrganizationID to each resource\r\n- [x] properly authorize based off of User ID and Org's permission - based on JWT\r\n  - [x] filter resources (Dashboards, Sources, & Rules) based on Organization\r\n- [x] write 3 generic tests for no org, diff org, and same org, + role-based access of the various resources at server level\r\n\r\nSection 3\r\n- [x] validate Organization and fill in `me` accordingly\r\n\r\nSection 4\r\n- [x] move creating default organization to bolt\r\n- [x] clean up adding first user to default organization\r\n- [x] make only first user superadmin - currently hacked in as all users\r\n- [x] add ability to make a user a superadmin to users CRUD\r\n\r\n### QUESTIONS FOR REVIEW\r\n- [x] ensure that scheme is \"oauth2\" in bolt? (oauth2 is stored in the jwt)\r\n- [x] ensure that provider is lowercase? (probably something that can be looked at later)\r\n- [x] should you be able to add a Role without an OrganizationID as an Admin? --> at least need to document that OrgID is required in all User requests, including when adding a Role (I think no... even \"public\" could be an org)\r\n- [x] factor out filter into func (not yet)\r\n  - [x] \"delete Roles that are not\" --> \"filter out\"\r\n- [x] documentation:\r\n  - [x] because we have not nested our resources, we have to access a resource to determine if a user can operate on it, so we do these checks:\r\n    1. on your current organization, do you have a role that allows you access to this resource? (one is the HTTP verb check)...\r\n    1. when we access this resource, is your current organization the organization of this resource? ( ... the other is the database check)\r\n- [x] double-check if Layouts need filtering – they seem to be read-only anyway (yes you need filtering)\r\n- [x] double-check if Servers need filtering (yes)","created_at":"2017-10-20T17:57:43Z","updated_at":"2017-11-02T13:40:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"daa02914bdbf0fa5b9fdddf67b47a3a09b7d87fa","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/influxdata/chronograf/pulls/2142/commits","review_comments_url":"https://api.github.com/repos/influxdata/chronograf/pulls/2142/comments","review_comment_url":"https://api.github.com/repos/influxdata/chronograf/pulls/comments{/number}","comments_url":"https://api.github.com/repos/influxdata/chronograf/issues/2142/comments","statuses_url":"https://api.github.com/repos/influxdata/chronograf/statuses/78754bdbdab8fc2e407b34b464edeb1316da298e","head":{"label":"influxdata:multitenancy_orgs","ref":"multitenancy_orgs","sha":"78754bdbdab8fc2e407b34b464edeb1316da298e","user":{"login":"influxdata","id":5713248,"avatar_url":"https://avatars2.githubusercontent.com/u/5713248?v=4","gravatar_id":"","url":"https://api.github.com/users/influxdata","html_url":"https://github.com/influxdata","followers_url":"https://api.github.com/users/influxdata/followers","following_url":"https://api.github.com/users/influxdata/following{/other_user}","gists_url":"https://api.github.com/users/influxdata/gists{/gist_id}","starred_url":"https://api.github.com/users/influxdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/influxdata/subscriptions","organizations_url":"https://api.github.com/users/influxdata/orgs","repos_url":"https://api.github.com/users/influxdata/repos","events_url":"https://api.github.com/users/influxdata/events{/privacy}","received_events_url":"https://api.github.com/users/influxdata/received_events","type":"Organization","site_admin":false},"repo":{"id":66506962,"name":"chronograf","full_name":"influxdata/chronograf","owner":{"login":"influxdata","id":5713248,"avatar_url":"https://avatars2.githubusercontent.com/u/5713248?v=4","gravatar_id":"","url":"https://api.github.com/users/influxdata","html_url":"https://github.com/influxdata","followers_url":"https://api.github.com/users/influxdata/followers","following_url":"https://api.github.com/users/influxdata/following{/other_user}","gists_url":"https://api.github.com/users/influxdata/gists{/gist_id}","starred_url":"https://api.github.com/users/influxdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/influxdata/subscriptions","organizations_url":"https://api.github.com/users/influxdata/orgs","repos_url":"https://api.github.com/users/influxdata/repos","events_url":"https://api.github.com/users/influxdata/events{/privacy}","received_events_url":"https://api.github.com/users/influxdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/influxdata/chronograf","description":"Open source monitoring and visualization UI for the TICK stack","fork":false,"url":"https://api.github.com/repos/influxdata/chronograf","forks_url":"https://api.github.com/repos/influxdata/chronograf/forks","keys_url":"https://api.github.com/repos/influxdata/chronograf/keys{/key_id}","collaborators_url":"https://api.github.com/repos/influxdata/chronograf/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/influxdata/chronograf/teams","hooks_url":"https://api.github.com/repos/influxdata/chronograf/hooks","issue_events_url":"https://api.github.com/repos/influxdata/chronograf/issues/events{/number}","events_url":"https://api.github.com/repos/influxdata/chronograf/events","assignees_url":"https://api.github.com/repos/influxdata/chronograf/assignees{/user}","branches_url":"https://api.github.com/repos/influxdata/chronograf/branches{/branch}","tags_url":"https://api.github.com/repos/influxdata/chronograf/tags","blobs_url":"https://api.github.com/repos/influxdata/chronograf/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/influxdata/chronograf/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/influxdata/chronograf/git/refs{/sha}","trees_url":"https://api.github.com/repos/influxdata/chronograf/git/trees{/sha}","statuses_url":"https://api.github.com/repos/influxdata/chronograf/statuses/{sha}","languages_url":"https://api.github.com/repos/influxdata/chronograf/languages","stargazers_url":"https://api.github.com/repos/influxdata/chronograf/stargazers","contributors_url":"https://api.github.com/repos/influxdata/chronograf/contributors","subscribers_url":"https://api.github.com/repos/influxdata/chronograf/subscribers","subscription_url":"https://api.github.com/repos/influxdata/chronograf/subscription","commits_url":"https://api.github.com/repos/influxdata/chronograf/commits{/sha}","git_commits_url":"https://api.github.com/repos/influxdata/chronograf/git/commits{/sha}","comments_url":"https://api.github.com/repos/influxdata/chronograf/comments{/number}","issue_comment_url":"https://api.github.com/repos/influxdata/chronograf/issues/comments{/number}","contents_url":"https://api.github.com/repos/influxdata/chronograf/contents/{+path}","compare_url":"https://api.github.com/repos/influxdata/chronograf/compare/{base}...{head}","merges_url":"https://api.github.com/repos/influxdata/chronograf/merges","archive_url":"https://api.github.com/repos/influxdata/chronograf/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/influxdata/chronograf/downloads","issues_url":"https://api.github.com/repos/influxdata/chronograf/issues{/number}","pulls_url":"https://api.github.com/repos/influxdata/chronograf/pulls{/number}","milestones_url":"https://api.github.com/repos/influxdata/chronograf/milestones{/number}","notifications_url":"https://api.github.com/repos/influxdata/chronograf/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/influxdata/chronograf/labels{/name}","releases_url":"https://api.github.com/repos/influxdata/chronograf/releases{/id}","deployments_url":"https://api.github.com/repos/influxdata/chronograf/deployments","created_at":"2016-08-24T23:28:56Z","updated_at":"2017-11-02T07:09:51Z","pushed_at":"2017-11-02T01:07:13Z","git_url":"git://github.com/influxdata/chronograf.git","ssh_url":"git@github.com:influxdata/chronograf.git","clone_url":"https://github.com/influxdata/chronograf.git","svn_url":"https://github.com/influxdata/chronograf","homepage":"https://www.influxdata.com/time-series-platform/chronograf/","size":43252,"stargazers_count":422,"watchers_count":422,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":93,"mirror_url":null,"archived":false,"open_issues_count":336,"forks":93,"open_issues":336,"watchers":422,"default_branch":"master"}},"base":{"label":"influxdata:multitenancy","ref":"multitenancy","sha":"10e55376f57ef34555e288f91e150143510ad44d","user":{"login":"influxdata","id":5713248,"avatar_url":"https://avatars2.githubusercontent.com/u/5713248?v=4","gravatar_id":"","url":"https://api.github.com/users/influxdata","html_url":"https://github.com/influxdata","followers_url":"https://api.github.com/users/influxdata/followers","following_url":"https://api.github.com/users/influxdata/following{/other_user}","gists_url":"https://api.github.com/users/influxdata/gists{/gist_id}","starred_url":"https://api.github.com/users/influxdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/influxdata/subscriptions","organizations_url":"https://api.github.com/users/influxdata/orgs","repos_url":"https://api.github.com/users/influxdata/repos","events_url":"https://api.github.com/users/influxdata/events{/privacy}","received_events_url":"https://api.github.com/users/influxdata/received_events","type":"Organization","site_admin":false},"repo":{"id":66506962,"name":"chronograf","full_name":"influxdata/chronograf","owner":{"login":"influxdata","id":5713248,"avatar_url":"https://avatars2.githubusercontent.com/u/5713248?v=4","gravatar_id":"","url":"https://api.github.com/users/influxdata","html_url":"https://github.com/influxdata","followers_url":"https://api.github.com/users/influxdata/followers","following_url":"https://api.github.com/users/influxdata/following{/other_user}","gists_url":"https://api.github.com/users/influxdata/gists{/gist_id}","starred_url":"https://api.github.com/users/influxdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/influxdata/subscriptions","organizations_url":"https://api.github.com/users/influxdata/orgs","repos_url":"https://api.github.com/users/influxdata/repos","events_url":"https://api.github.com/users/influxdata/events{/privacy}","received_events_url":"https://api.github.com/users/influxdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/influxdata/chronograf","description":"Open source monitoring and visualization UI for the TICK stack","fork":false,"url":"https://api.github.com/repos/influxdata/chronograf","forks_url":"https://api.github.com/repos/influxdata/chronograf/forks","keys_url":"https://api.github.com/repos/influxdata/chronograf/keys{/key_id}","collaborators_url":"https://api.github.com/repos/influxdata/chronograf/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/influxdata/chronograf/teams","hooks_url":"https://api.github.com/repos/influxdata/chronograf/hooks","issue_events_url":"https://api.github.com/repos/influxdata/chronograf/issues/events{/number}","events_url":"https://api.github.com/repos/influxdata/chronograf/events","assignees_url":"https://api.github.com/repos/influxdata/chronograf/assignees{/user}","branches_url":"https://api.github.com/repos/influxdata/chronograf/branches{/branch}","tags_url":"https://api.github.com/repos/influxdata/chronograf/tags","blobs_url":"https://api.github.com/repos/influxdata/chronograf/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/influxdata/chronograf/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/influxdata/chronograf/git/refs{/sha}","trees_url":"https://api.github.com/repos/influxdata/chronograf/git/trees{/sha}","statuses_url":"https://api.github.com/repos/influxdata/chronograf/statuses/{sha}","languages_url":"https://api.github.com/repos/influxdata/chronograf/languages","stargazers_url":"https://api.github.com/repos/influxdata/chronograf/stargazers","contributors_url":"https://api.github.com/repos/influxdata/chronograf/contributors","subscribers_url":"https://api.github.com/repos/influxdata/chronograf/subscribers","subscription_url":"https://api.github.com/repos/influxdata/chronograf/subscription","commits_url":"https://api.github.com/repos/influxdata/chronograf/commits{/sha}","git_commits_url":"https://api.github.com/repos/influxdata/chronograf/git/commits{/sha}","comments_url":"https://api.github.com/repos/influxdata/chronograf/comments{/number}","issue_comment_url":"https://api.github.com/repos/influxdata/chronograf/issues/comments{/number}","contents_url":"https://api.github.com/repos/influxdata/chronograf/contents/{+path}","compare_url":"https://api.github.com/repos/influxdata/chronograf/compare/{base}...{head}","merges_url":"https://api.github.com/repos/influxdata/chronograf/merges","archive_url":"https://api.github.com/repos/influxdata/chronograf/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/influxdata/chronograf/downloads","issues_url":"https://api.github.com/repos/influxdata/chronograf/issues{/number}","pulls_url":"https://api.github.com/repos/influxdata/chronograf/pulls{/number}","milestones_url":"https://api.github.com/repos/influxdata/chronograf/milestones{/number}","notifications_url":"https://api.github.com/repos/influxdata/chronograf/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/influxdata/chronograf/labels{/name}","releases_url":"https://api.github.com/repos/influxdata/chronograf/releases{/id}","deployments_url":"https://api.github.com/repos/influxdata/chronograf/deployments","created_at":"2016-08-24T23:28:56Z","updated_at":"2017-11-02T07:09:51Z","pushed_at":"2017-11-02T01:07:13Z","git_url":"git://github.com/influxdata/chronograf.git","ssh_url":"git@github.com:influxdata/chronograf.git","clone_url":"https://github.com/influxdata/chronograf.git","svn_url":"https://github.com/influxdata/chronograf","homepage":"https://www.influxdata.com/time-series-platform/chronograf/","size":43252,"stargazers_count":422,"watchers_count":422,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":93,"mirror_url":null,"archived":false,"open_issues_count":336,"forks":93,"open_issues":336,"watchers":422,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/influxdata/chronograf/pulls/2142"},"html":{"href":"https://github.com/influxdata/chronograf/pull/2142"},"issue":{"href":"https://api.github.com/repos/influxdata/chronograf/issues/2142"},"comments":{"href":"https://api.github.com/repos/influxdata/chronograf/issues/2142/comments"},"review_comments":{"href":"https://api.github.com/repos/influxdata/chronograf/pulls/2142/comments"},"review_comment":{"href":"https://api.github.com/repos/influxdata/chronograf/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/influxdata/chronograf/pulls/2142/commits"},"statuses":{"href":"https://api.github.com/repos/influxdata/chronograf/statuses/78754bdbdab8fc2e407b34b464edeb1316da298e"}},"author_association":"NONE"}}
{"actor":{"display_login":"desa"}}