@openneuro/server 4.37.0 → 4.38.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openneuro/server",
3
- "version": "4.37.0",
3
+ "version": "4.38.0-alpha.1",
4
4
  "description": "Core service for the OpenNeuro platform.",
5
5
  "license": "MIT",
6
6
  "main": "src/server.js",
@@ -21,7 +21,7 @@
21
21
  "@elastic/elasticsearch": "8.13.1",
22
22
  "@graphql-tools/schema": "^10.0.0",
23
23
  "@keyv/redis": "^4.5.0",
24
- "@openneuro/search": "^4.37.0",
24
+ "@openneuro/search": "^4.38.0-alpha.1",
25
25
  "@sentry/node": "^8.25.0",
26
26
  "@sentry/profiling-node": "^8.25.0",
27
27
  "base64url": "^3.0.0",
@@ -88,5 +88,5 @@
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
91
- "gitHead": "998a4fa9b988103cebd227483cede442d1b80962"
91
+ "gitHead": "c7a1350704df419d17999699e056030b9aa0bd66"
92
92
  }
@@ -138,13 +138,18 @@ export const checkDatasetWrite = async (
138
138
  // Quick path for anonymous writes
139
139
  throw new Error(state.errorMessage)
140
140
  }
141
- if (userId && !(userInfo.email)) {
142
- throw new Error("Connect an email to make contributions to OpenNeuro.")
143
- }
144
141
  if (userId && userInfo.admin) {
145
142
  // Always allow site admins
146
143
  return true
147
144
  }
145
+ // Allow worker scoped tokens to make admin actions on specific datasets
146
+ if (userId && userInfo?.worker && datasetId === userInfo?.dataset) {
147
+ return true
148
+ }
149
+ if (userId && !(userInfo.email)) {
150
+ throw new Error("Connect an email to make contributions to OpenNeuro.")
151
+ }
152
+ // Finally check the permissions model if other checks have not returned
148
153
  const permission = await Permission.findOne({ datasetId, userId }).exec()
149
154
  if (checkPermissionLevel(permission, state)) {
150
155
  return true
@@ -164,29 +164,31 @@ const parseQuery = async (query, datasetType, datasetStatus, userId) => {
164
164
  },
165
165
  }
166
166
  addClause(query, "filter", termsClause)
167
-
168
- if (datasetStatus === "Public") {
169
- addClause(query, "filter", {
170
- term: {
171
- public: {
172
- value: true,
167
+ // Add logic to explicitly check for the "All" status
168
+ if (datasetStatus && datasetStatus !== "All") {
169
+ if (datasetStatus === "Public") {
170
+ addClause(query, "filter", {
171
+ term: {
172
+ public: {
173
+ value: true,
174
+ },
173
175
  },
174
- },
175
- })
176
- } else if (datasetStatus === "Shared with Me") {
177
- addClause(query, "filter", {
178
- terms: {
179
- ["permissions.userPermissions.level"]: ["ro", "rw"],
180
- },
181
- })
182
- } else if (datasetStatus === "Invalid") {
183
- addClause(query, "filter", {
184
- range: {
185
- "latestSnapshot.validation.errors": {
186
- gt: 0,
176
+ })
177
+ } else if (datasetStatus === "Shared with Me") {
178
+ addClause(query, "filter", {
179
+ terms: {
180
+ ["permissions.userPermissions.level"]: ["ro", "rw"],
187
181
  },
188
- },
189
- })
182
+ })
183
+ } else if (datasetStatus === "Invalid") {
184
+ addClause(query, "filter", {
185
+ range: {
186
+ "latestSnapshot.validation.errors": {
187
+ gt: 0,
188
+ },
189
+ },
190
+ })
191
+ }
190
192
  }
191
193
  }
192
194
  return query
@@ -0,0 +1,17 @@
1
+ import FileCheck from "../../models/fileCheck"
2
+ import { checkDatasetAdmin } from "../permissions"
3
+
4
+ export const updateFileCheck = async (
5
+ obj,
6
+ { datasetId, hexsha, refs, remote, annexFsck },
7
+ { user, userInfo },
8
+ ) => {
9
+ await checkDatasetAdmin(datasetId, user, userInfo)
10
+ return await FileCheck.findOneAndUpdate(
11
+ { datasetId, hexsha },
12
+ { datasetId, hexsha, remote, refs, annexFsck },
13
+ { upsert: true, new: true },
14
+ )
15
+ .lean()
16
+ .exec()
17
+ }
@@ -45,6 +45,7 @@ import {
45
45
  } from "./importRemoteDataset"
46
46
  import { saveAdminNote } from "./datasetEvents"
47
47
  import { createGitEvent } from "./gitEvents"
48
+ import { updateFileCheck } from "./fileCheck"
48
49
 
49
50
  const Mutation = {
50
51
  createDataset,
@@ -93,6 +94,7 @@ const Mutation = {
93
94
  updateUser,
94
95
  saveAdminNote,
95
96
  createGitEvent,
97
+ updateFileCheck,
96
98
  }
97
99
 
98
100
  export default Mutation
@@ -205,6 +205,14 @@ export const typeDefs = `
205
205
  saveAdminNote(id: ID, datasetId: ID!, note: String!): DatasetEvent
206
206
  # Create a git event log for dataset changes
207
207
  createGitEvent(datasetId: ID!, commit: String!, reference: String!): DatasetEvent
208
+ # Create or update a fileCheck document
209
+ updateFileCheck(
210
+ datasetId: ID!
211
+ hexsha: String!
212
+ refs: [String!]!
213
+ annexFsck: [AnnexFsckInput!]!
214
+ remote: String
215
+ ): FileCheck
208
216
  }
209
217
 
210
218
  # Anonymous dataset reviewer
@@ -900,6 +908,33 @@ export const typeDefs = `
900
908
  # Notes associated with the event
901
909
  note: String
902
910
  }
911
+
912
+ type FileCheck {
913
+ datasetId: String!
914
+ hexsha: String!
915
+ refs: [String!]!
916
+ annexFsck: [AnnexFsck!]
917
+ remote: String
918
+ }
919
+
920
+ type AnnexFsck {
921
+ command: String
922
+ errorMessages: [String]
923
+ file: String
924
+ key: String
925
+ note: String
926
+ success: Boolean
927
+ }
928
+
929
+ input AnnexFsckInput {
930
+ command: String
931
+ errorMessages: [String]
932
+ file: String
933
+ key: String
934
+ note: String
935
+ success: Boolean
936
+ }
937
+
903
938
  `
904
939
 
905
940
  schemaComposer.addTypeDefs(typeDefs)
@@ -120,6 +120,14 @@ export const setupPassportAuth = () => {
120
120
  (jwt, done) => {
121
121
  if (jwt.scopes?.includes("dataset:indexing")) {
122
122
  done(null, { admin: false, blocked: false, indexer: true })
123
+ } else if (jwt.scopes?.includes("dataset:worker")) {
124
+ done(null, {
125
+ id: jwt.sub,
126
+ admin: false,
127
+ blocked: false,
128
+ worker: true,
129
+ dataset: jwt.dataset,
130
+ })
123
131
  } else if (jwt.scopes?.includes("dataset:reviewer")) {
124
132
  done(null, {
125
133
  admin: false,
@@ -0,0 +1,37 @@
1
+ import mongoose from "mongoose"
2
+ import type { Document } from "mongoose"
3
+ const { Schema, model } = mongoose
4
+
5
+ export interface FileCheckDocument extends Document {
6
+ datasetId: string
7
+ hexsha: string
8
+ refs: string[]
9
+ remote: string
10
+ annexFsck: {
11
+ command: string
12
+ "error-messages": string[]
13
+ file: string
14
+ key: string
15
+ note: string
16
+ success: boolean
17
+ }[]
18
+ }
19
+
20
+ const fileCheckSchema = new Schema({
21
+ datasetId: { type: String, required: true },
22
+ hexsha: { type: String, required: true },
23
+ refs: { type: [String], required: true },
24
+ remote: { type: String, default: "local", required: true },
25
+ annexFsck: [{
26
+ command: String,
27
+ "error-messages": [String],
28
+ file: String,
29
+ key: String,
30
+ note: String,
31
+ success: Boolean,
32
+ }],
33
+ })
34
+
35
+ const FileCheck = model<FileCheckDocument>("FileCheck", fileCheckSchema)
36
+
37
+ export default FileCheck