11FROM php:8.1-alpine
22
3- RUN apk add --no-cache git jq moreutils
4- RUN apk add --no-cache $PHPIZE_DEPS postgresql-dev \
5- && docker-php-ext-install pdo_pgsql \
3+ # Base tools and PHP extensions
4+ RUN apk add --no-cache git jq moreutils \
5+ && apk add --no-cache $PHPIZE_DEPS postgresql-dev sqlite-dev \
6+ && docker-php-ext-install pdo_pgsql pdo_sqlite \
67 && pecl install xdebug-3.1.5 \
78 && docker-php-ext-enable xdebug \
89 && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
910 && echo "xdebug.client_host = 172.19.0.1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
1011
12+ # Composer + fresh Laravel app
1113RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
12- RUN composer create-project --prefer-dist laravel/laravel example && \
13- cd example
14+ RUN composer create-project --prefer-dist laravel/laravel example && cd example
1415
1516WORKDIR /example
1617
18+ # Link local package
1719COPY . /laravel-scim-server
1820RUN jq '.repositories=[{"type": "path","url": "/laravel-scim-server"}]' ./composer.json | sponge ./composer.json
1921
22+ # Install package and dev helpers
2023RUN composer require arietimmerman/laravel-scim-server @dev && \
2124 composer require laravel/tinker
2225
26+ # SQLite config
2327RUN touch /example/database.sqlite && \
2428 echo "DB_CONNECTION=sqlite" >> /example/.env && \
2529 echo "DB_DATABASE=/example/database.sqlite" >> /example/.env && \
2630 echo "APP_URL=http://localhost:18123" >> /example/.env
2731
32+ # Make users.password nullable to allow SCIM-created users without passwords
33+ RUN sed -i -E "s/\\ $table->string\( 'password'\) ;/\\ $table->string('password')->nullable();/g" \
34+ database/migrations/*create_users_table.php || true
2835
29- # Add migration for groups table using heredoc
36+ # Groups table migration
3037RUN cat > /example/database/migrations/2021_01_01_000001_create_groups_table.php <<'EOM'
3138<?php
3239
@@ -51,24 +58,31 @@ return new class extends Migration {
5158};
5259EOM
5360
54- # Add Group model
61+ # Group model with members() relation
5562RUN cat > app/Models/Group.php <<'EOM'
5663<?php
5764
5865namespace App\M odels;
5966
6067use Illuminate\D atabase\E loquent\M odel;
6168use Illuminate\D atabase\E loquent\F actories\H asFactory;
69+ use Illuminate\D atabase\E loquent\R elations\B elongsToMany;
70+ use App\M odels\U ser;
6271
6372class Group extends Model
6473{
6574 use HasFactory;
6675
6776 protected $fillable = ['displayName' ];
77+
78+ public function members(): BelongsToMany
79+ {
80+ return $this->belongsToMany(User::class, 'group_user' , 'group_id' , 'user_id' )->withTimestamps();
81+ }
6882}
6983EOM
7084
71- # Add Custom SCIM Config overriding Group model class
85+ # Override SCIM config to use app's Group model
7286RUN mkdir -p app/SCIM && cat > app/SCIM/CustomSCIMConfig.php <<'EOM'
7387<?php
7488
@@ -81,14 +95,13 @@ class CustomSCIMConfig extends BaseSCIMConfig
8195 public function getGroupConfig()
8296 {
8397 $config = parent::getGroupConfig();
84- // Force the group model to the example app's Group model
8598 $config['class' ] = \A pp\M odels\G roup::class;
8699 return $config;
87100 }
88101}
89102EOM
90103
91- # Override AppServiceProvider to register custom SCIMConfig binding
104+ # Bind CustomSCIMConfig in the container
92105RUN cat > app/Providers/AppServiceProvider.php <<'EOM'
93106<?php
94107
@@ -107,12 +120,12 @@ class AppServiceProvider extends ServiceProvider
107120
108121 public function boot(): void
109122 {
110- // Additional boot logic if needed
123+ //
111124 }
112125}
113126EOM
114127
115- # Add Group factory
128+ # Group factory
116129RUN cat > database/factories/GroupFactory.php <<'EOM'
117130<?php
118131
@@ -134,8 +147,97 @@ class GroupFactory extends Factory
134147}
135148EOM
136149
150+ # Pivot table for memberships
151+ RUN cat > /example/database/migrations/2021_01_01_000002_create_group_user_table.php <<'EOM'
152+ <?php
153+
154+ use Illuminate\D atabase\M igrations\M igration;
155+ use Illuminate\D atabase\S chema\B lueprint;
156+ use Illuminate\S upport\F acades\S chema;
157+
158+ return new class extends Migration {
159+ public function up(): void
160+ {
161+ Schema::create('group_user' , function (Blueprint $table) {
162+ $table->id();
163+ $table->foreignId('group_id' )->constrained('groups' )->cascadeOnDelete();
164+ $table->foreignId('user_id' )->constrained('users' )->cascadeOnDelete();
165+ $table->timestamps();
166+ $table->unique(['group_id' , 'user_id' ]);
167+ });
168+ }
169+
170+ public function down(): void
171+ {
172+ Schema::dropIfExists('group_user' );
173+ }
174+ };
175+ EOM
176+
177+ # Ensure User model has groups() relation (overwrite default)
178+ RUN cat > app/Models/User.php <<'EOM'
179+ <?php
180+
181+ namespace App\M odels;
182+
183+ use Illuminate\D atabase\E loquent\F actories\H asFactory;
184+ use Illuminate\F oundation\A uth\U ser as Authenticatable;
185+ use Illuminate\N otifications\N otifiable;
186+ use Laravel\S anctum\H asApiTokens;
187+ use Illuminate\D atabase\E loquent\R elations\B elongsToMany;
188+
189+ class User extends Authenticatable
190+ {
191+ use HasApiTokens, HasFactory, Notifiable;
192+
193+ protected $fillable = [
194+ 'name' ,
195+ 'email' ,
196+ 'password' ,
197+ ];
198+
199+ protected $hidden = [
200+ 'password' ,
201+ 'remember_token' ,
202+ ];
203+
204+ protected $casts = [
205+ 'email_verified_at' => 'datetime' ,
206+ ];
207+
208+ public function groups(): BelongsToMany
209+ {
210+ return $this->belongsToMany(Group::class, 'group_user' , 'user_id' , 'group_id' )->withTimestamps();
211+ }
212+ }
213+ EOM
214+
215+ # Seeder for demo data
216+ RUN cat > /example/database/seeders/DemoSeeder.php <<'EOM'
217+ <?php
218+
219+ namespace Database\S eeders;
220+
221+ use Illuminate\D atabase\S eeder;
222+ use App\M odels\U ser;
223+ use App\M odels\G roup;
224+
225+ class DemoSeeder extends Seeder
226+ {
227+ public function run(): void
228+ {
229+ $users = User::factory()->count(50)->create();
230+ $groups = Group::factory()->count(10)->create();
231+
232+ foreach ($groups as $g) {
233+ $g->members()->sync($users->random(rand(3, 10))->pluck('id' )->toArray());
234+ }
235+ }
236+ }
237+ EOM
238+
137239# Run migrations and seed demo data
138- RUN php artisan migrate && \
139- echo "User::factory()->count(100)->create(); App\\ Models\\ Group::factory()->count(10)->create();" | php artisan tinker
240+ RUN php artisan migrate && php artisan db:seed --class=Database\\ Seeders\\ DemoSeeder
140241
141242CMD ["php" ,"artisan" ,"serve" ,"--host=0.0.0.0" ,"--port=8000" ]
243+
0 commit comments