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



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

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



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

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



66
67
68
# File 'lib/git-lfs-s3/application.rb', line 66

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

#lfs_resp(objects) ⇒ Object



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

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



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

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



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

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



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

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
# 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



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

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



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

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)


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

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