Class: GitLfsS3::Application

Inherits:
Sinatra::Application
  • Object
show all
Includes:
AwsHelpers
Defined in:
lib/git-lfs-s3/application.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from AwsHelpers

#aws_access_key_id, #aws_region, #aws_secret_access_key, #bucket, #bucket_name, #object_data, #s3

Class Attribute Details

.auth_callbackObject (readonly)

Returns the value of attribute auth_callback.



11
12
13
# File 'lib/git-lfs-s3/application.rb', line 11

def auth_callback
  @auth_callback
end

Class Method Details

.authentication_enabled?Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/git-lfs-s3/application.rb', line 17

def authentication_enabled?
  !auth_callback.nil?
end

.on_authenticate(&block) ⇒ Object



13
14
15
# File 'lib/git-lfs-s3/application.rb', line 13

def on_authenticate(&block)
  @auth_callback = block
end

.perform_authentication(username, password, is_safe) ⇒ Object



21
22
23
# File 'lib/git-lfs-s3/application.rb', line 21

def perform_authentication(username, password, is_safe)
  auth_callback.call(username, password, is_safe)
end

Instance Method Details

#authorized?Boolean

Returns:

  • (Boolean)


37
38
39
40
41
42
43
44
45
# File 'lib/git-lfs-s3/application.rb', line 37

def authorized?
  @auth ||= Rack::Auth::Basic::Request.new(request.env)
  @auth.provided? &&
    @auth.basic? &&
    @auth.credentials &&
    self.class.auth_callback.call(
      @auth.credentials[0], @auth.credentials[1], request.safe?
    )
end

#download(authenticated, params) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/git-lfs-s3/application.rb', line 121

def download(authenticated, params)
  # Handle git-lfs batch downloads.
  objects = []
  params[:objects].each do |obj_json|
    obj_json = Sinatra::IndifferentHash[obj_json]
    obj = object_data(obj_json[:oid])
    if valid_obj?(obj_json)
      if obj.exists?
        objects.push(obj_download(authenticated, obj, obj_json))
      else
        objects.push(obj_error(404, 'Object does not exist', obj_json))
      end
    else
      objects.push(obj_error(422, 'Validation error', obj_json))
    end
  end
  objects
end

#error_resp(status_code, message) ⇒ Object



169
170
171
172
173
174
175
176
177
# File 'lib/git-lfs-s3/application.rb', line 169

def error_resp(status_code, message)
  # Error git-lfs batch response.
  status(status_code)
  resp = {
    'message'    => message,
    'request_id' => SecureRandom.uuid
  }
  body MultiJson.dump(resp)
end

#expire_atObject



68
69
70
# File 'lib/git-lfs-s3/application.rb', line 68

def expire_at
  DateTime.now.next_day.to_time.utc.iso8601
end

#lfs_resp(objects) ⇒ Object



159
160
161
162
163
164
165
166
167
# File 'lib/git-lfs-s3/application.rb', line 159

def lfs_resp(objects)
  # Successful git-lfs batch response.
  status(200)
  resp = {
    'transfer' => 'basic',
    'objects'  => objects
  }
  body MultiJson.dump(resp)
end

#obj_download(authenticated, obj, obj_json) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/git-lfs-s3/application.rb', line 72

def obj_download(authenticated, obj, obj_json)
  # Format a single download object.
  oid = obj_json[:oid]
  size = obj_json[:size]
  {
    'oid'           => oid,
    'size'          => size,
    'authenticated' => authenticated,
    'actions'       => {
      'download' => {
        'href'       => obj.presigned_url(:get,
                                          expires_in: 86_400),
        'expires_at' => expire_at,
      },
    },
  }
end

#obj_error(error, message, obj_json) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
# File 'lib/git-lfs-s3/application.rb', line 109

def obj_error(error, message, obj_json)
  # Format a single error object.
  {
    'oid'   => obj_json[:oid],
    'size'  => obj_json[:size],
    'error' => {
      'code'    => error,
      'message' => message,
    },
  }
end

#obj_upload(authenticated, obj, obj_json) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/git-lfs-s3/application.rb', line 90

def obj_upload(authenticated, obj, obj_json)
  # Format a single upload object.
  oid = obj_json[:oid]
  size = obj_json[:size]
  {
    'oid'           => oid,
    'size'          => size,
    'authenticated' => authenticated,
    'actions'       => {
      'upload' => {
        'href'       => obj.presigned_url(:put,
                                          acl:        'public-read',
                                          expires_in: 86_400),
        'expires_at' => expire_at,
      },
    },
  }
end

#protected!Object



47
48
49
50
51
52
# File 'lib/git-lfs-s3/application.rb', line 47

def protected!
  return unless authorized?

  response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
  throw(:halt, [401, 'Invalid username or password'])
end

#public_read_grantObject



235
236
237
238
239
240
241
# File 'lib/git-lfs-s3/application.rb', line 235

def public_read_grant
  grantee = Aws::S3::Types::Grantee.new(
    display_name: nil, email_address: nil, id: nil, type: nil,
    uri: 'http://acs.amazonaws.com/groups/global/AllUsers'
  )
  Aws::S3::Types::Grant.new(grantee: grantee, permission: 'READ')
end

#upload(authenticated, params) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/git-lfs-s3/application.rb', line 140

def upload(authenticated, params)
  # Handle git-lfs batch uploads.
  objects = []
  params[:objects].each do |obj_json|
    obj_json = Sinatra::IndifferentHash[obj_json]
    obj = object_data(obj_json[:oid])
    if valid_obj?(obj_json)
      if obj.exists?
        objects.push(obj_download(authenticated, obj, obj_json))
      else
        objects.push(obj_upload(authenticated, obj, obj_json))
      end
    else
      objects.push(obj_error(422, 'Validation error', obj_json))
    end
  end
  objects
end

#valid_obj?(obj) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
63
64
65
66
# File 'lib/git-lfs-s3/application.rb', line 58

def valid_obj?(obj)
  # Validate that size >= 0 and oid is a SHA256 hash.
  oid = obj[:oid]
  return unless obj[:size] >= 0

  (oid.hex.size <= 32) && (oid.size == 64) && (oid =~ /^[0-9a-f]+$/)
  # XXX what exception needs to be ignored here?
rescue Exception
end